From a61f7ab532d6d2fd9f237145f91bbcc9043431f6 Mon Sep 17 00:00:00 2001 From: Abdelrahman Awad Date: Tue, 31 Aug 2021 23:03:31 +0200 Subject: [PATCH] fix: added silent validation run after reset closes #3463 --- packages/vee-validate/src/useField.ts | 1 + packages/vee-validate/tests/Field.spec.ts | 4 +- packages/vee-validate/tests/Form.spec.ts | 2 +- packages/vee-validate/tests/useField.spec.ts | 53 ++++++++++++++------ packages/vee-validate/tests/useForm.spec.ts | 25 +++++++-- 5 files changed, 63 insertions(+), 22 deletions(-) diff --git a/packages/vee-validate/src/useField.ts b/packages/vee-validate/src/useField.ts index 04cdb0974..8b5d0f40f 100644 --- a/packages/vee-validate/src/useField.ts +++ b/packages/vee-validate/src/useField.ts @@ -195,6 +195,7 @@ export function useField( function resetField(state?: Partial>) { unwatchValue?.(); resetValidationState(state); + validateValidStateOnly(); // need to watch at next tick to avoid triggering the value watcher nextTick(() => { watchValue(); diff --git a/packages/vee-validate/tests/Field.spec.ts b/packages/vee-validate/tests/Field.spec.ts index 07c3fe0bc..969975afe 100644 --- a/packages/vee-validate/tests/Field.spec.ts +++ b/packages/vee-validate/tests/Field.spec.ts @@ -872,7 +872,7 @@ describe('', () => { expect(input.value).toBe(modelValue); }); - test('resetField should reset the valid flag to true', async () => { + test('resetField should reset the valid flag to false if the rules are incorrect', async () => { const wrapper = mountWithHoc({ template: `
@@ -896,7 +896,7 @@ describe('', () => { wrapper.$el.querySelector('button').click(); await flushPromises(); - expect(meta?.textContent).toBe('valid'); + expect(meta?.textContent).toBe('invalid'); }); test('valid flag is synced with the field errors array length', async () => { diff --git a/packages/vee-validate/tests/Form.spec.ts b/packages/vee-validate/tests/Form.spec.ts index 0bddb179e..e9d6ece9e 100644 --- a/packages/vee-validate/tests/Form.spec.ts +++ b/packages/vee-validate/tests/Form.spec.ts @@ -1610,7 +1610,7 @@ describe('
', () => { expect(span.textContent).toBe('invalid'); wrapper.$el.querySelector('button').click(); await flushPromises(); - expect(span.textContent).toBe('valid'); + expect(span.textContent).toBe('invalid'); }); test('resetForm should reset the meta flag based on the errors length', async () => { diff --git a/packages/vee-validate/tests/useField.spec.ts b/packages/vee-validate/tests/useField.spec.ts index 49295d631..93d01a7cb 100644 --- a/packages/vee-validate/tests/useField.spec.ts +++ b/packages/vee-validate/tests/useField.spec.ts @@ -4,6 +4,7 @@ import { mountWithHoc, setValue } from './helpers'; describe('useField()', () => { const REQUIRED_MESSAGE = 'Field is required'; + const MIN_MESSAGE = 'Field must be at least 3'; test('validates when value changes', async () => { mountWithHoc({ setup() { @@ -28,38 +29,60 @@ describe('useField()', () => { expect(error?.textContent).toBe(REQUIRED_MESSAGE); }); - test('valid flag is true after reset', async () => { + test('valid flag is correct after reset', async () => { mountWithHoc({ setup() { - const { value, meta, resetField } = useField('field', val => (val ? true : REQUIRED_MESSAGE)); + const { + value: value1, + meta: meta1, + resetField: reset1, + } = useField('field', val => (val ? true : REQUIRED_MESSAGE)); + const { + value: value2, + meta: meta2, + resetField: reset2, + } = useField('field', val => (!val || (val as string).length >= 3 ? true : MIN_MESSAGE)); return { - value, - meta, - resetField, + value1, + value2, + meta1, + meta2, + reset1, + reset2, }; }, template: ` - - {{ meta.valid ? 'valid' : 'invalid' }} - + + {{ meta1.valid ? 'valid' : 'invalid' }} + + + {{ meta2.valid ? 'valid' : 'invalid' }} + `, }); - const input = document.querySelector('input') as HTMLInputElement; - const meta = document.querySelector('#meta'); + const input1 = document.querySelector('#input1') as HTMLInputElement; + const meta1 = document.querySelector('#meta1'); + const input2 = document.querySelector('#input2') as HTMLInputElement; + const meta2 = document.querySelector('#meta2'); await flushPromises(); - expect(meta?.textContent).toBe('invalid'); + expect(meta1?.textContent).toBe('invalid'); + expect(meta2?.textContent).toBe('valid'); - setValue(input, ''); + setValue(input1, '12'); + setValue(input2, '12'); await flushPromises(); - expect(meta?.textContent).toBe('invalid'); + expect(meta1?.textContent).toBe('valid'); + expect(meta2?.textContent).toBe('invalid'); // trigger reset - document.querySelector('button')?.click(); + (document.querySelector('#r1') as HTMLButtonElement).click(); + (document.querySelector('#r2') as HTMLButtonElement).click(); await flushPromises(); - expect(meta?.textContent).toBe('valid'); + expect(meta1?.textContent).toBe('invalid'); + expect(meta2?.textContent).toBe('valid'); }); test('valid flag is synced with fields errors length', async () => { diff --git a/packages/vee-validate/tests/useForm.spec.ts b/packages/vee-validate/tests/useForm.spec.ts index 095213b15..e991ca824 100644 --- a/packages/vee-validate/tests/useForm.spec.ts +++ b/packages/vee-validate/tests/useForm.spec.ts @@ -2,6 +2,7 @@ import flushPromises from 'flush-promises'; import { FormContext, useField, useForm } from '@/vee-validate'; import { mountWithHoc, setValue } from './helpers'; import * as yup from 'yup'; +import { Ref } from 'vue'; describe('useForm()', () => { const REQUIRED_MESSAGE = 'Field is required'; @@ -284,36 +285,52 @@ describe('useForm()', () => { }); test('resets the meta valid state on reset', async () => { + let passwordValue!: Ref; mountWithHoc({ setup() { - const { meta: formMeta, resetForm } = useForm(); + const { meta: formMeta, resetForm, errors } = useForm(); const { value } = useField('field', val => (val ? true : REQUIRED_MESSAGE)); - useField('password', val => (val ? true : REQUIRED_MESSAGE)); + const { value: pwValue } = useField('password', val => (val ? true : REQUIRED_MESSAGE)); + passwordValue = pwValue; return { value, formMeta, resetForm, + errors, }; }, template: ` {{ formMeta.valid ? 'valid': 'invalid' }} + {{ errors }} `, }); await flushPromises(); const span = document.querySelector('#meta'); + const errors = document.querySelector('#errors'); const input = document.querySelector('input') as HTMLInputElement; expect(span?.textContent).toBe('invalid'); - setValue(input, ''); + setValue(input, '12'); await flushPromises(); + // still other field is invalid expect(span?.textContent).toBe('invalid'); + // but the error is silent so errors should be empty + expect(errors?.textContent).toBe('{}'); - document.querySelector('button')?.click(); + passwordValue.value = '12'; await flushPromises(); + // now both should be valid expect(span?.textContent).toBe('valid'); + expect(errors?.textContent).toBe('{}'); + + document.querySelector('button')?.click(); + await flushPromises(); + // validation was run again silently + expect(span?.textContent).toBe('invalid'); + expect(errors?.textContent).toBe('{}'); }); test('resets the meta valid state on reset with the errors length', async () => {