Repository: icebob/fastest-validator
Pull request #290 (2022-03-15 09:59:30)

fix(multi): item rule has custom checker will throw error if validate…
… failed

custom checker push errors to fnCustomErrorsX that will be assigned to var errors, not _errors as expected
To avoid a breaking changes, I just keep size of errors before validate and restore it if failed
But I think the logic should be changed
![image](https://user-images.githubusercontent.com/32500015/158353324-eb324481-6988-4ae8-9123-3a9830c4a6f7.png)



Patch:
diff --git a/lib/rules/multi.js b/lib/rules/multi.js
index 378518b..5955f16 100644
--- a/lib/rules/multi.js
+++ b/lib/rules/multi.js
@@ -9,6 +9,7 @@ module.exports = function({ schema, messages }, path, context) {
 		var hasValid = false;
 		var newVal = value;
 		var checkErrors = [];
+		var errorsSize = errors.length;
 	`);
 
 	for (let i = 0; i < schema.rules.length; i++) {
@@ -20,11 +21,11 @@ module.exports = function({ schema, messages }, path, context) {
 		const rule = this.getRuleFromSchema(schema.rules[i]);
 		src.push(this.compileRule(rule, context, path, `var tmpVal = ${context.async ? "await " : ""}context.fn[%%INDEX%%](value, field, parent, _errors, context);`, "tmpVal"));
 		src.push(`
-				if (_errors.length == 0) {
+				if (errors.length == errorsSize && _errors.length == 0) {
 					hasValid = true;
 					newVal = tmpVal;
 				} else {
-					Array.prototype.push.apply(checkErrors, _errors);
+					Array.prototype.push.apply(checkErrors, [].concat(_errors, errors.splice(errorsSize)));
 				}
 			}
 		`);


Test patch:
diff --git a/test/rules/multi.spec.js b/test/rules/multi.spec.js
index 1ed666b..53986ff 100644
--- a/test/rules/multi.spec.js
+++ b/test/rules/multi.spec.js
@@ -88,4 +88,74 @@ describe("Test rule: multi", () => {
 			expect(check({d: 4})).toEqual([{"actual": undefined, "field": "a", "message": "The 'a' field is required.", "type": "required"}, {"actual": undefined, "field": "b", "message": "The 'b' field is required.", "type": "required"}, {"actual": undefined, "field": "c", "message": "The 'c' field is required.", "type": "required"}]);
 		});
 	});
+
+	describe("should work with custom validator", () => {
+		const checkerFn = jest.fn(() => {});
+
+		const v = new Validator({
+			useNewCustomCheckerFunction: true,
+			aliases: {
+				strOK: {
+					type: "string",
+					custom: (value, errors) => {
+						checkerFn();
+						if (value !== "OK") {
+							errors.push({type: "strOK"});
+							return;
+						}
+						return value;
+					}
+				},
+				num99: {
+					type: "number",
+					custom: (value, errors) => {
+						checkerFn();
+						if (value !== 99) {
+							errors.push({type: "num99"});
+							return;
+						}
+						return value;
+					}
+				}
+			}
+		});
+
+		const schema = {
+			a: {
+				type: "multi",
+				rules: ["strOK", "num99"]
+			}
+		};
+		const check = v.compile(schema);
+
+		it("test strOK", () => {
+			{
+				const o = { a: "OK" };
+				expect(check(o)).toBe(true);
+				expect(o).toStrictEqual({ a: "OK" });
+				expect(checkerFn).toBeCalledTimes(1);
+			}
+			{
+				const o = { a: "not-OK" };
+				expect(check(o)).toStrictEqual([{"field": "a", "message": undefined, "type": "strOK"}, {"actual": "not-OK", "field": "a", "message": "The 'a' field must be a number.", "type": "number"}, {"field": "a", "message": undefined, "type": "num99"}]);
+				expect(o).toStrictEqual({ a: "not-OK" });
+				expect(checkerFn).toBeCalledTimes(3);
+			}
+		});
+
+		it("test num99", () => {
+			{
+				const o = { a: 99 };
+				expect(check(o)).toBe(true);
+				expect(o).toStrictEqual({ a: 99 });
+				expect(checkerFn).toBeCalledTimes(5);
+			}
+			{
+				const o = { a: 1199 };
+				expect(check(o)).toStrictEqual([{"actual": 1199, "field": "a", "message": "The 'a' field must be a string.", "type": "string"}, {"field": "a", "message": undefined, "type": "strOK"}, {"field": "a", "message": undefined, "type": "num99"}]);
+				expect(o).toStrictEqual({ a: 1199 });
+				expect(checkerFn).toBeCalledTimes(7);
+			}
+		});
+	});
 });