Skip to content

Commit

Permalink
minor(vest): support schema skip and only (#557)
Browse files Browse the repository at this point in the history
  • Loading branch information
ealush committed Jan 21, 2021
1 parent 0cf5615 commit 8458837
Show file tree
Hide file tree
Showing 14 changed files with 589 additions and 30 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.

8 changes: 6 additions & 2 deletions packages/n4s/src/compounds/isArrayOf.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,15 @@ function isArrayOf(value, ruleChains) {
}

for (let i = 0; i < plainValue.length; i++) {
// Set result per each item in the array|
// Set result per each item in the array
result.setChild(
i,
runCompoundChain(
EnforceContext.wrap(plainValue[i]).setFailFast(value.failFast),
new EnforceContext({
value: plainValue[i],
obj: plainValue,
key: i,
}).setFailFast(value.failFast),
ruleChains,
{ mode: MODE_ANY }
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,67 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Rule with "when" Should skip all excluded array items 1`] = `
RuleResult {
"children": Object {
"items": RuleResult {
"children": Object {
"0": RuleResult {
"children": Object {
"checked": RuleResult {
"failed": false,
"hasErrors": false,
"hasWarnings": false,
"warn": false,
},
},
"failed": false,
"hasErrors": false,
"hasWarnings": false,
"when": false,
},
"3": RuleResult {
"children": Object {
"checked": RuleResult {
"failed": false,
"hasErrors": false,
"hasWarnings": false,
"warn": false,
},
},
"failed": false,
"hasErrors": false,
"hasWarnings": false,
"when": false,
},
},
"failed": false,
"hasErrors": false,
"hasWarnings": false,
"isArray": true,
},
},
"failed": false,
"hasErrors": false,
"hasWarnings": false,
}
`;

exports[`Rule with "when" Should skip excluded object items 1`] = `
RuleResult {
"children": Object {
"example1": RuleResult {
"failed": false,
"hasErrors": false,
"hasWarnings": false,
"warn": false,
},
},
"failed": false,
"hasErrors": false,
"hasWarnings": false,
}
`;

exports[`Rule with a message Should add message to result 1`] = `
RuleResult {
"children": Object {
Expand Down
89 changes: 89 additions & 0 deletions packages/n4s/src/meta/__tests__/ruleMeta.test.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import RuleResult from 'RuleResult';
import enforce from 'enforce';

describe('Rule with a message', () => {
Expand Down Expand Up @@ -62,3 +63,91 @@ describe('Rule with a warning', () => {
expect(res).toMatchSnapshot();
});
});

describe('Rule with "when"', () => {
it('Should skip all excluded array items', () => {
const Schema = enforce.shape({
items: enforce.isArrayOf(
enforce.shape({ checked: enforce.isBoolean() }).when(value => {
return value.checked;
})
),
});

const res = Schema.run({
items: [
{ checked: true },
{ checked: false },
{ checked: false },
{ checked: true },
],
});

expect(res.children.items.children[0]).toBeInstanceOf(RuleResult);
expect(res.children.items.children[1]).toBeUndefined();
expect(res.children.items.children[2]).toBeUndefined();
expect(res.children.items.children[3]).toBeInstanceOf(RuleResult);

expect(res).toMatchSnapshot();
});

it('Should skip excluded object items', () => {
const Schema = enforce.shape({
example: enforce.isString().when(() => false),
example1: enforce.isString(),
});

const res = Schema.run({
example: 'something',
example1: 'something',
});

expect(res.children.example).toBeUndefined();
expect(res.children.example1).toBeInstanceOf(RuleResult);

expect(res).toMatchSnapshot();
});
it('Should pass each item the value, key and parent', () => {
const whenItems = jest.fn();
const whenName = jest.fn();
const Schema = enforce.shape({
name: enforce
.shape({
first: enforce.isString(),
})
.when(whenName),
items: enforce.isArrayOf(
enforce.shape({ checked: enforce.isBoolean() }).when(whenItems)
),
});

const data = {
name: {
first: 'example',
},
items: [
{ checked: true },
{ checked: false },
{ checked: false },
{ checked: true },
],
};

Schema.run(data);

expect(whenName).toHaveBeenCalledWith(data.name, 'name', data);

expect(whenItems.mock.calls[0][0]).toBe(data.items[0]);
expect(whenItems.mock.calls[1][0]).toBe(data.items[1]);
expect(whenItems.mock.calls[2][0]).toBe(data.items[2]);
expect(whenItems.mock.calls[3][0]).toBe(data.items[3]);
expect(whenItems.mock.calls[0][1]).toBe(0); // key
expect(whenItems.mock.calls[1][1]).toBe(1); // key
expect(whenItems.mock.calls[2][1]).toBe(2); // key
expect(whenItems.mock.calls[3][1]).toBe(3); // key
expect(whenItems.mock.calls[0][2]).toBe(data.items); // parent
expect(whenItems.mock.calls[1][2]).toBe(data.items); // parent
expect(whenItems.mock.calls[2][2]).toBe(data.items); // parent
expect(whenItems.mock.calls[3][2]).toBe(data.items); // parent
});
});
4 changes: 3 additions & 1 deletion packages/n4s/src/meta/ruleMeta.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@

import message from 'ruleMessage';
import warn from 'ruleWarn';
import when from 'ruleWhen';

export default { warn, message };
export default { warn, message, when };
13 changes: 13 additions & 0 deletions packages/n4s/src/meta/ruleWhen.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import EnforceContext from 'EnforceContext';
import optionalFunctionValue from 'optionalFunctionValue';

export default function when(value, condition, bail) {
const shouldBail = !optionalFunctionValue(
condition,
[EnforceContext.unwrap(value)].concat(
EnforceContext.is(value) ? [value.key, value.obj] : []
)
);

return bail(shouldBail);
}
9 changes: 9 additions & 0 deletions packages/n4s/src/runtime/RuleResult.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import hasOwnProperty from 'hasOwnProperty';

import { isBoolean } from 'isBoolean';
import { isEmpty } from 'isEmpty';
import { isNull } from 'isNull';
import { isUndefined } from 'isUndefined';
import { HAS_WARNINGS, HAS_ERRORS } from 'sharedKeys';

Expand Down Expand Up @@ -65,6 +66,10 @@ RuleResult.prototype.setFailed = function (failed) {
* @param {RuleResult} child
*/
RuleResult.prototype.setChild = function (key, child) {
if (isNull(child)) {
return null;
}

const isWarning =
this[HAS_WARNINGS] || child[HAS_WARNINGS] || child.warn || this.warn;
this.setAttribute(HAS_WARNINGS, (isWarning && child.failed) || false);
Expand Down Expand Up @@ -96,6 +101,10 @@ RuleResult.prototype.getChild = function (key) {
* @param {Boolean|RuleResult} newRes
*/
RuleResult.prototype.extend = function (newRes) {
if (isNull(newRes)) {
return this;
}

const res = RuleResult.is(newRes)
? newRes
: new RuleResult().setAttribute('warn', !!this.warn).setFailed(!newRes);
Expand Down
12 changes: 9 additions & 3 deletions packages/n4s/src/runtime/bindLazyRule.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ export default function bindLazyRule(ruleName) {

// Add a meta function
if (ruleMeta[rule.name] === rule) {
meta.push((value, ruleResult) => {
ruleResult.setAttribute(rule.name, rule(value, args[0]));
meta.push((value, ruleResult, bail) => {
ruleResult.setAttribute(rule.name, rule(value, args[0], bail));
});
} else {
// Register a rule
Expand All @@ -57,11 +57,17 @@ export default function bindLazyRule(ruleName) {
returnvalue[RUN_RULE] = value => {
const result = new RuleResult(true);

let bailed = false;

// Run meta chains
meta.forEach(fn => {
fn(value, result);
fn(value, result, shouldBail => (bailed = shouldBail));
});

if (bailed) {
return null;
}

// Iterate over all the registered rules
// This runs the function that's inside `addFn`
for (const fn of registeredRules) {
Expand Down
5 changes: 5 additions & 0 deletions packages/n4s/src/runtime/runCompoundChain.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import RuleResult from 'RuleResult';
import { MODE_ALL, MODE_ONE, MODE_ANY } from 'enforceKeywords';
import { isEmpty } from 'isEmpty';
import { isNull } from 'isNull';
import { runLazyRule } from 'runLazyRules';

/**
Expand All @@ -24,6 +25,10 @@ export default function runCompoundChain(value, rules, options) {
// Inner result for each iteration
const currentResult = runLazyRule(chain, value);

if (isNull(currentResult)) {
return null;
}

const pass = currentResult.pass;

if (pass) {
Expand Down
4 changes: 3 additions & 1 deletion packages/vest/src/__tests__/test_types/fixtures/rules.ts

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

35 changes: 19 additions & 16 deletions packages/vest/src/typings/schema.d.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,26 @@
declare function getFn(fieldName: string): string[];
declare function getFn(): { [fieldName: string]: string[] };

declare function schema(
enforceSchema: any
): (
data: any
) => {
hasErrors: (fieldName?: string) => boolean;
hasWarnings: (fieldName?: string) => boolean;
getErrors: typeof getFn;
getWarnings: typeof getFn;
tests: {
[key: string]: {
hasErrors: boolean;
hasWarnings: boolean;
warnings: string[];
errors: string[];
interface schema {
(enforceSchema: any, body?: (...args: any[]) => void): (
data: any
) => {
hasErrors: (fieldName?: string) => boolean;
hasWarnings: (fieldName?: string) => boolean;
getErrors: typeof getFn;
getWarnings: typeof getFn;
tests: {
[key: string]: {
hasErrors: boolean;
hasWarnings: boolean;
warnings: string[];
errors: string[];
};
};
};
};

skip: (namespace: string) => void;
only: (namespace: string) => void;
}

export default schema;
Loading

0 comments on commit 8458837

Please sign in to comment.