Skip to content

Commit

Permalink
feat: flesh out value parsing on required
Browse files Browse the repository at this point in the history
  • Loading branch information
jeffhandley committed Dec 10, 2017
1 parent 8c0c73e commit fdb8f7b
Show file tree
Hide file tree
Showing 3 changed files with 144 additions and 27 deletions.
37 changes: 21 additions & 16 deletions src/required.js
Expand Up @@ -6,27 +6,32 @@ export default function required(props) {
return function validate(value) {
let isValid = true;

if (value === null || value === notDefined) {
const parse = typeof props.parseValue === 'function' ?
props.parseValue : parseValue;

const parsedValue = parse(value);

if (parsedValue === null || parsedValue === notDefined) {
isValid = false;
} else if (typeof value === 'string') {
if (props.trim !== false) {
if (typeof props.trim === 'function') {
value = props.trim(value);
} else {
value = value.trim();
}
}

isValid = !!value.length;
} else if (typeof value === 'boolean') {
// By supporting required on boolean values where false is invalid
// we open up scenarios for required checkboxes
isValid = value;
} else if (typeof parsedValue === 'string') {
isValid = !!parsedValue.length;
} else if (typeof parsedValue === 'boolean') {
isValid = parsedValue;
}

return {
...props,
isValid
isValid,
value,
parsedValue
};
}
}

function parseValue(value) {
if (typeof value === 'string') {
return value.trim();
}

return value;
}
128 changes: 120 additions & 8 deletions test/required.spec.js
Expand Up @@ -22,17 +22,12 @@ describe('required', () => {
expect(result.isValid).toBe(false);
});

it('is not valid for all spaces', () => {
const result = validate(' ');
expect(result.isValid).toBe(false);
});

it('is valid for non-empty strings', () => {
const result = validate('not empty');
expect(result.isValid).toBe(true);
});

it('is valid for the number 0', () => {
it('is valid for the number 0 (because 0 is indeed a supplied number)', () => {
const result = validate(0);
expect(result.isValid).toBe(true);
});
Expand All @@ -47,10 +42,27 @@ describe('required', () => {
expect(result.isValid).toBe(true);
});

it('is not valid for boolean false', () => {
it('is not valid for boolean false (supporting scenarios like required checkboxes)', () => {
const result = validate(false);
expect(result.isValid).toBe(false);
});

it('trims string values so that an all spaces value is not valid', () => {
const result = validate(' ');
expect(result.isValid).toBe(false);
});

describe('exposes values on the result object', () => {
it('for the original value', () => {
const result = validate(' Original ');
expect(result.value).toBe(' Original ');
});

it('for the parsed (trimmed) value', () => {
const result = validate(' Original ');
expect(result.parsedValue).toBe('Original');
});
});
});

describe('with props', () => {
Expand All @@ -68,7 +80,7 @@ describe('required', () => {
});
});

describe('overrides isValid prop', () => {
describe('overrides isValid prop with the validation result', () => {
it('when valid', () => {
const validate = required({isValid: false});
const result = validate('Valid');
Expand All @@ -82,4 +94,104 @@ describe('required', () => {
});
});
});

describe('value parsing can be overridden with a parseValue prop', () => {
it('affecting validity to make an invalid value valid', () => {
const parseValue = () => 'Valid';
const validate = required({parseValue});

const result = validate('');
expect(result.isValid).toBe(true);
});

it('affecting validity to make a valid value invalid', () => {
const parseValue = () => '';
const validate = required({parseValue});

const result = validate('Valid');
expect(result.isValid).toBe(false);
});

it('with the original value supplied to the parseValue function', () => {
let suppliedValue;

function parseValue(value) {
suppliedValue = value;
return value;
}

const validate = required({parseValue});
const result = validate('Original');

expect(suppliedValue).toBe('Original');
});

it('with the parsed value exposed as the parsedValue prop', () => {
const parseValue = (value) => 'Parsed: ' + value;
const validate = required({parseValue});

const result = validate('Value');
expect(result.parsedValue).toBe('Parsed: Value');
});

it('bypassing string trimming', () => {
const parseValue = (value) => value;
const validate = required({parseValue});

const result = validate(' ');
expect(result.parsedValue).toBe(' ');
});

describe('converting a value', () => {
it('from null to a string validates the string', () => {
const parseValue = () => 'Valid';
const validate = required({parseValue});

const result = validate(null);
expect(result.isValid).toBe(true);
});

describe('from a string to a boolean validates the boolean', () => {
it('that is valid', () => {
const parseValue = () => true;
const validate = required({parseValue});

const result = validate(' ');
expect(result.isValid).toBe(true);
});

it('that is invalid', () => {
const parseValue = () => false;
const validate = required({parseValue});

const result = validate(' ');
expect(result.isValid).toBe(false);
});
});

it('from a string to null', () => {
const parseValue = () => null;
const validate = required({parseValue});

const result = validate('Valid');
expect(result.isValid).toBe(false);
});

it('from a string to undefined', () => {
const parseValue = () => {};
const validate = required({parseValue});

const result = validate('Valid');
expect(result.isValid).toBe(false);
});

it('from a string to a number', () => {
const parseValue = () => 0;
const validate = required({parseValue});

const result = validate('');
expect(result.isValid).toBe(true);
});
});
});
});
6 changes: 3 additions & 3 deletions test/strickland.spec.js
Expand Up @@ -116,7 +116,7 @@ describe('validate', () => {
expect(isValid(result)).toBe(false);
});

it('returns an object with an isValid prop set to false if the object does not specify isValid', () => {
it('returns an object with isValid = false if the object does not specify isValid', () => {
const ruleResult = {
message: 'That is not valid'
};
Expand Down Expand Up @@ -145,7 +145,7 @@ describe('validate', () => {
});
});

it('returns an object with an isValid prop set to true if the object has isValid set to a truthy value other than true', () => {
it('returns an object with isValid = true if the object has isValid set to a truthy value other than true', () => {
const ruleResult = {
message: 'That is not valid',
isValid: 'Yep'
Expand All @@ -169,7 +169,7 @@ describe('validate', () => {
expect(isValid(result)).toBe(false);
});

it('returns an object with an isValid prop set to false if the object has isValid set to a falsy value other than false', () => {
it('returns an object with isValid = false if the object has isValid set to a falsy value other than false', () => {
const ruleResult = {
message: 'That is not valid',
isValid: 0
Expand Down

0 comments on commit fdb8f7b

Please sign in to comment.