Skip to content

Commit

Permalink
Merge pull request #306 from Abdullah0sama/add-label-field
Browse files Browse the repository at this point in the history
Using labels in error messages
  • Loading branch information
icebob authored Aug 30, 2022
2 parents e273136 + 87f8dda commit 9ca1503
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 10 deletions.
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,28 @@ const v = new FastestValidator({
}
});
```
# Label Option
You can use label names in error messages instead of property names.
```js
const schema = {
email: { type: "email", label: "Email Address" },
};
const check = v.compile(schema);

console.log(check({ email: "notAnEmail" }));

/* Returns
[
{
type: 'email',
message: "The 'Email Address' field must be a valid e-mail.",
field: 'email',
actual: 'notAnEmail',
label: 'Email Address'
}
]
*/
```
# Built-in validators

## `any`
Expand Down
7 changes: 5 additions & 2 deletions lib/rules/object.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,16 @@ module.exports = function ({ schema, messages }, path, context) {
const safePropName = `parentObj${safeSubName}`;
const newPath = (path ? path + "." : "") + property;

const labelName = subSchema[property].label;
const label = labelName ? `'${escapeEvalString(labelName)}'` : undefined;

sourceCode.push(`\n// Field: ${escapeEvalString(newPath)}`);
sourceCode.push(`field = parentField ? parentField + "${safeSubName}" : "${name}";`);
sourceCode.push(`value = ${safePropName};`);

sourceCode.push(`label = ${label}`);
const rule = this.getRuleFromSchema(subSchema[property]);
const innerSource = `
${safePropName} = ${context.async ? "await " : ""}context.fn[%%INDEX%%](value, field, parentObj, errors, context);
${safePropName} = ${context.async ? "await " : ""}context.fn[%%INDEX%%](value, field, parentObj, errors, context, label);
`;
sourceCode.push(this.compileRule(rule, context, newPath, innerSource, safePropName));
if (this.opts.haltOnFirstError === true) {
Expand Down
10 changes: 6 additions & 4 deletions lib/validator.js
Original file line number Diff line number Diff line change
Expand Up @@ -201,20 +201,21 @@ class Validator {
"var errors = [];",
"var field;",
"var parent = null;",
`var label = ${schema.label ? "\"" + schema.label + "\"" : "null"};`
];

const rule = this.getRuleFromSchema(schema);
sourceCode.push(this.compileRule(rule, context, null, `${context.async ? "await " : ""}context.fn[%%INDEX%%](value, field, null, errors, context);`, "value"));
sourceCode.push(this.compileRule(rule, context, null, `${context.async ? "await " : ""}context.fn[%%INDEX%%](value, field, null, errors, context, label);`, "value"));

sourceCode.push("if (errors.length) {");
sourceCode.push(`
return errors.map(err => {
if (err.message) {
err.message = context.utils.replace(err.message, /\\{field\\}/g, err.field);
err.message = context.utils.replace(err.message, /\\{field\\}/g, err.label || err.field);
err.message = context.utils.replace(err.message, /\\{expected\\}/g, err.expected);
err.message = context.utils.replace(err.message, /\\{actual\\}/g, err.actual);
}
if(!err.label) delete err.label
return err;
});
`);
Expand Down Expand Up @@ -282,7 +283,7 @@ class Validator {
const res = rule.ruleFunction.call(this, rule, path, context);
res.source = res.source.replace(/%%INDEX%%/g, rule.index);
const FnClass = context.async ? AsyncFunction : Function;
const fn = new FnClass("value", "field", "parent", "errors", "context", res.source);
const fn = new FnClass("value", "field", "parent", "errors", "context", "label", res.source);
context.fn[rule.index] = fn.bind(this);
sourceCode.push(this.wrapRequiredCheckSourceCode(rule, innerSrc.replace(/%%INDEX%%/g, rule.index), context, resVar));
sourceCode.push(this.makeCustomValidator({vName: resVar, path: customPath, schema: rule.schema, context, messages: rule.messages, ruleIndex: rule.index}));
Expand Down Expand Up @@ -380,6 +381,7 @@ class Validator {
else o.field = "field";
if (expected != null) o.expected = expected;
if (actual != null) o.actual = actual;
o.label = "label";

const s = Object.keys(o)
.map(key => `${key}: ${o[key]}`)
Expand Down
4 changes: 2 additions & 2 deletions test/typescript/validator.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -229,10 +229,10 @@ describe('TypeScript Definitions', () => {

it('should generate an error creation code', () => {
expect(v.makeError({ type: 'required', messages: v.messages })).
toBe('errors.push({ type: "required", message: "The \'{field}\' field is required.", field: field });');
toBe('errors.push({ type: "required", message: "The \'{field}\' field is required.", field: field, label: label });');
expect(v.makeError({ type: 'stringMin', field: 'firstName', expected: 6, actual: 3, messages: v.messages })).
toBe(
'errors.push({ type: "stringMin", message: "The \'{field}\' field length must be greater than or equal to {expected} characters long.", field: "firstName", expected: 6, actual: 3 });');
'errors.push({ type: "stringMin", message: "The \'{field}\' field length must be greater than or equal to {expected} characters long.", field: "firstName", expected: 6, actual: 3, label: label });');
});

});
Expand Down
20 changes: 18 additions & 2 deletions test/validator.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -294,8 +294,8 @@ describe("Test makeError", () => {
const v = new Validator();

it("should generate an error creation code", () => {
expect(v.makeError({ type: "required", messages: v.messages })).toBe("errors.push({ type: \"required\", message: \"The '{field}' field is required.\", field: field });");
expect(v.makeError({ type: "stringMin", field: "firstName", expected: 6, actual: 3, messages: v.messages })).toBe("errors.push({ type: \"stringMin\", message: \"The '{field}' field length must be greater than or equal to {expected} characters long.\", field: \"firstName\", expected: 6, actual: 3 });");
expect(v.makeError({ type: "required", messages: v.messages })).toBe("errors.push({ type: \"required\", message: \"The '{field}' field is required.\", field: field, label: label });");
expect(v.makeError({ type: "stringMin", field: "firstName", expected: 6, actual: 3, messages: v.messages })).toBe("errors.push({ type: \"stringMin\", message: \"The '{field}' field length must be greater than or equal to {expected} characters long.\", field: \"firstName\", expected: 6, actual: 3, label: label });");
});

});
Expand Down Expand Up @@ -340,6 +340,22 @@ describe("Test compile (integration test)", () => {

});

describe("Test label in error message instead of property names", () => {
const v = new Validator();
const schema = {
email: { type: "email", label: "Email Address" },
};

let check = v.compile(schema);

it("Should return message with label value", () => {
let res = check({});

expect(res[0].label).toBe(schema.email.label);
expect(res[0].message).toBe("The 'Email Address' field is required.");
});
});

describe("Test check generator with wrong obj and haltOnFirstError", () => {
const v = new Validator({ haltOnFirstError: true });

Expand Down

0 comments on commit 9ca1503

Please sign in to comment.