Repository: baianat/vee-validate
Pull request #1245 (2018-04-01 13:43:10)

Add silent validation run
Details: https://github.com/baianat/vee-validate/issues/1243


Patch:
diff --git a/src/core/validator.js b/src/core/validator.js
index fca2ecf2..a1c9f314 100644
--- a/src/core/validator.js
+++ b/src/core/validator.js
@@ -292,7 +292,7 @@ export default class Validator {
   /**
    * Validates a value against a registered field validations.
    */
-  async validate (name: string, value: any, scope?: string | null = null): Promise<boolean> {
+  async validate (name: string, value: any, scope?: string | null = null, silent?: boolean = false): Promise<boolean> {
     if (this.paused) return Promise.resolve(true);
 
     // overload to validate all.
@@ -316,16 +316,16 @@ export default class Validator {
       return this._handleFieldNotFound(name, scope);
     }
 
-    field.flags.pending = true;
+    if (!silent) field.flags.pending = true;
     if (arguments.length === 1) {
       value = field.value;
     }
 
-    const silentRun = field.isDisabled;
+    const result = await this._validate(field, value, silent);
+    if (silent) return result.valid;
 
-    const result = await this._validate(field, value, silentRun);
     this.errors.remove(field.name, field.scope, field.id);
-    if (silentRun) {
+    if (field.isDisabled) {
       return true;
     } else if (result.errors) {
       result.errors.forEach(e => this.errors.add(e));
@@ -361,7 +361,7 @@ export default class Validator {
   /**
    * Validates each value against the corresponding field validations.
    */
-  async validateAll (values?: string | MapObject): Promise<boolean> {
+  async validateAll (values?: string | MapObject, scope?: string | null = null, silent?: boolean = false): Promise<boolean> {
     if (this.paused) return true;
 
     let matcher = null;
@@ -371,20 +371,22 @@ export default class Validator {
       matcher = { scope: values };
     } else if (isObject(values)) {
       matcher = Object.keys(values).map(key => {
-        return { name: key, scope: arguments[1] || null };
+        return { name: key, scope };
       });
       providedValues = true;
-    } else if (arguments.length === 0) {
-      matcher = { scope: null }; // global scope.
     } else if (Array.isArray(values)) {
       matcher = values.map(key => {
-        return { name: key, scope: arguments[1] || null };
+        return { name: key, scope };
       });
+    } else {
+      matcher = { scope };
     }
 
     const promises = this.fields.filter(matcher).map(field => this.validate(
       `#${field.id}`,
-      providedValues ? values[field.name] : field.value
+      providedValues ? values[field.name] : field.value,
+      null,
+      silent
     ));
 
     const results = await Promise.all(promises);
@@ -395,12 +397,14 @@ export default class Validator {
   /**
    * Validates all scopes.
    */
-  async validateScopes (): Promise<boolean> {
+  async validateScopes (silent?: boolean = false): Promise<boolean> {
     if (this.paused) return true;
 
     const promises = this.fields.map(field => this.validate(
       `#${field.id}`,
-      field.value
+      field.value,
+      null,
+      silent
     ));
 
     const results = await Promise.all(promises);
diff --git a/types/vee-validate.d.ts b/types/vee-validate.d.ts
index e09dda85..f1f91755 100644
--- a/types/vee-validate.d.ts
+++ b/types/vee-validate.d.ts
@@ -147,9 +147,9 @@ export class Validator {
     localize(rootDictionary?: Object) :void;
     localize(language: string, dictionary?: Object) :void;
     setStrictMode(strictMode?: boolean): void;
-    validate(name: string, value?: any, scope?: string): Promise<any>;
-    validateAll(values?: Object, scope?: string): Promise<any>;
-    validateScopes(): Promise<any>;
+    validate(name: string, value?: any, scope?: string, silent?: boolean): Promise<any>;
+    validateAll(values?: Object, scope?: string, silent?: boolean): Promise<any>;
+    validateScopes(silent?: boolean): Promise<any>;
     static create(validations: Object, options: any): Validator;
     static extend(name: string, validator: Object|Function): void;
     static remove(name: string): void;


Test patch:
diff --git a/tests/unit/validator.js b/tests/unit/validator.js
index 43f1fdcb..b12424c6 100644
--- a/tests/unit/validator.js
+++ b/tests/unit/validator.js
@@ -569,7 +569,7 @@ test('can remove rules from the list of validators', async () => {
   await expect(v1.validate('name', false)).rejects.toBeTruthy();
 });
 
-test('calling validate without args will trigger validateAll', async () => {
+test('calling validate without args will trigger validateScopes', async () => {
   const v = new Validator();
   v.validateScopes = jest.fn(async () => {});
 
@@ -577,7 +577,7 @@ test('calling validate without args will trigger validateAll', async () => {
   expect(v.validateScopes).toHaveBeenCalled();
 });
 
-test('calling validate with * will trigger validateScopes', async () => {
+test('calling validate with * will trigger validateAll', async () => {
   const v = new Validator();
   v.validateAll = jest.fn(async () => { });
 
@@ -660,6 +660,83 @@ test('validations can be paused and resumed', async () => {
   })).toBe(false);
 });
 
+test('errors will not be added on silent validate', async () => {
+  const v = new Validator({
+    name: 'alpha|min:3'
+  }, { fastExit: false });
+  expect(await v.validate('name', '2', null, true)).toBe(false);
+  expect(v.errors.count()).toBe(0);
+});
+
+test('errors will not be removed on silent validate', async () => {
+  const v = new Validator({
+    name: 'alpha|min:3'
+  }, { fastExit: false });
+  expect(await v.validate('name', '2', null, false)).toBe(false);
+  expect(await v.validate('name', '2', null, true)).toBe(false);
+  expect(v.errors.count()).toBe(2);
+});
+
+test('flags will not be modified on silent validate', async () => {
+  const v = new Validator({}, { fastExit: false });
+  const field = v.attach({ name: 'name', rules: 'alpha|min:3' });
+  expect(field.flags.pending).toBe(false);
+  expect(field.flags.validated).toBe(false);
+  expect(await v.validate('name', '2', null, true)).toBe(false);
+  expect(field.flags.pending).toBe(false);
+  expect(field.flags.validated).toBe(false);
+});
+
+test('errors will not be added on silent validateScopes', async () => {
+  const v = new Validator({}, { fastExit: false });
+  v.attach({ name: 'field', rules: 'alpha|min:3', getter: () => '2' });
+  expect(await v.validateScopes(true)).toBe(false);
+  expect(v.errors.count()).toBe(0);
+});
+
+test('errors will not be removed on silent validateScopes', async () => {
+  const v = new Validator({}, { fastExit: false });
+  v.attach({ name: 'field', rules: 'alpha|min:3', getter: () => '2' });
+  expect(await v.validateScopes(false)).toBe(false);
+  expect(await v.validateScopes(true)).toBe(false);
+  expect(v.errors.count()).toBe(2);
+});
+
+test('flags will not be modified on silent validateScopes', async () => {
+  const v = new Validator({}, { fastExit: false });
+  const field = v.attach({ name: 'field', rules: 'alpha|min:3', getter: () => '2' });
+  expect(field.flags.pending).toBe(false);
+  expect(field.flags.validated).toBe(false);
+  expect(await v.validateScopes(true)).toBe(false);
+  expect(field.flags.pending).toBe(false);
+  expect(field.flags.validated).toBe(false);
+});
+
+test('errors will not be added on silent validateAll', async () => {
+  const v = new Validator({}, { fastExit: false });
+  v.attach({ name: 'field', rules: 'alpha|min:3', getter: () => '2' });
+  expect(await v.validateAll(null, null, true)).toBe(false);
+  expect(v.errors.count()).toBe(0);
+});
+
+test('errors will not be removed on silent validateAll', async () => {
+  const v = new Validator({}, { fastExit: false });
+  v.attach({ name: 'field', rules: 'alpha|min:3', getter: () => '2' });
+  expect(await v.validateAll(null, null, false)).toBe(false);
+  expect(await v.validateAll(null, null, true)).toBe(false);
+  expect(v.errors.count()).toBe(2);
+});
+
+test('flags will not be modified on silent validateAll', async () => {
+  const v = new Validator({}, { fastExit: false });
+  const field = v.attach({ name: 'field', rules: 'alpha|min:3', getter: () => '2' });
+  expect(field.flags.pending).toBe(false);
+  expect(field.flags.validated).toBe(false);
+  expect(await v.validateAll(null, null, true)).toBe(false);
+  expect(field.flags.pending).toBe(false);
+  expect(field.flags.validated).toBe(false);
+});
+
 test('it can hold multiple errors for one field', async () => {
   const v = new Validator({
     name: 'alpha|min:3'