Skip to content

Commit

Permalink
added: [N4S] oneOf compound (#526)
Browse files Browse the repository at this point in the history
  • Loading branch information
hpsharon committed Nov 30, 2020
1 parent 6d1230a commit db7f6f4
Show file tree
Hide file tree
Showing 6 changed files with 115 additions and 0 deletions.
1 change: 1 addition & 0 deletions jsconfig.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 21 additions & 0 deletions packages/n4s/docs/compound.md
Expand Up @@ -129,3 +129,24 @@ enforce({ data: [1, 2, 3] }).shape({
data: enforce.isArrayOf(enforce.isNumber()),
});
```

## enforce.oneOf()

enforce.oneOf can be used to determine if _exactly_ one of the rules applies. It will run against rule in the array, and will only pass if exactly one rule applies.

```js
enforce(value).oneOf(
enforce.isString(),
enforce.isNumber(),
enforce.longerThan(1)
);

/*
value = 1 -> ✅ (value is a number)
value = "1" -> ✅ (value is string)
value = [1, 2] -> ✅ (value is longer than 1)
value = "12" -> 🚨 (value is both a string and longer than 1)
*/

```

66 changes: 66 additions & 0 deletions packages/n4s/src/enforce/compounds/__tests__/oneOf.test.js
@@ -0,0 +1,66 @@
import enforce from 'enforce';
import oneOf from 'oneOf';

describe('OneOf validation', () => {
describe('Base behavior', () => {
it('Should fail when all rules fail', () => {
expect(
oneOf(
'test',
enforce.isNumber(),
enforce.isUndefined()
)
).toBe(false);
});
it('Should succeed when EXACTLY one rule applies', () => {
expect(
oneOf(
5,
enforce.isString(),
enforce.isNumber(),
enforce.isUndefined()
)
).toBe(true);
});
it('Should fail when more than one rule applies', () => {
expect(
oneOf(
5,
enforce.isNumber(),
enforce.isNumber().greaterThan(3),
)
).toBe(false);
});
it('Should succeed when rule chaining', () => {
expect(
oneOf(
[1, 2, 3],
enforce.isArray().isNotEmpty().longerThan(2),
enforce.isUndefined()
)
).toBe(true);
});
it('Should fail with no rules', () => {
expect(
oneOf(5)
).toBe(false);
});
});

describe('As part of enforce', () => {
it('Should validate the rules correctly', () => {
enforce(77).oneOf(
enforce.isString(),
enforce.isNumber(),
enforce.isUndefined()
)

expect(() =>
enforce({ test: 4 }).oneOf(
enforce.isNumber(),
enforce.isUndefined()
)
).toThrow();
});
});
});
2 changes: 2 additions & 0 deletions packages/n4s/src/enforce/compounds/compounds.js
@@ -1,5 +1,6 @@
import anyOf from 'anyOf';
import isArrayOf from 'isArrayOf';
import oneOf from 'oneOf';
import optional from 'optional';
import { shape, loose } from 'shape';

Expand All @@ -9,4 +10,5 @@ export default {
loose,
optional,
shape,
oneOf
};
23 changes: 23 additions & 0 deletions packages/n4s/src/enforce/compounds/oneOf.js
@@ -0,0 +1,23 @@
import runLazyRules from 'runLazyRules';
import { withFirst } from 'withArgs';

/**
* @param {*} value Value to be test against rules
* @param {Function[]} rules Rules to validate the value with
*/
function oneOf(value, rules) {
let count = 0;

for (let i = 0; i < rules.length; i++) {
if (runLazyRules(rules[i], value)) {
count++;
}

if (count > 1) {
return false;
}
}

return count === 1;
}
export default withFirst(oneOf)
2 changes: 2 additions & 0 deletions packages/vest/src/typings/enforce.d.ts
Expand Up @@ -88,6 +88,7 @@ interface IEnforceRules<T = {}> {
) => RuleReturn<T>;
isArrayOf: CompoundListOfRules<T>;
anyOf: CompoundListOfRules<T>;
oneOf: CompoundListOfRules<T>;
}

interface IEnforce {
Expand Down Expand Up @@ -194,6 +195,7 @@ type TEnforceLazy = {
optional: LazyCopmoundListOfRules;
isArrayOf: LazyCopmoundListOfRules;
anyOf: LazyCopmoundListOfRules;
oneOf: LazyCopmoundListOfRules;
};

export type TEnforce = IEnforce & TEnforceLazyReturn;

0 comments on commit db7f6f4

Please sign in to comment.