Skip to content

Commit

Permalink
fix: added silent validation run after reset closes #3463
Browse files Browse the repository at this point in the history
  • Loading branch information
logaretm committed Aug 31, 2021
1 parent 2c4a7ff commit a61f7ab
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 22 deletions.
1 change: 1 addition & 0 deletions packages/vee-validate/src/useField.ts
Expand Up @@ -195,6 +195,7 @@ export function useField<TValue = unknown>(
function resetField(state?: Partial<FieldState<TValue>>) {
unwatchValue?.();
resetValidationState(state);
validateValidStateOnly();
// need to watch at next tick to avoid triggering the value watcher
nextTick(() => {
watchValue();
Expand Down
4 changes: 2 additions & 2 deletions packages/vee-validate/tests/Field.spec.ts
Expand Up @@ -872,7 +872,7 @@ describe('<Field />', () => {
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: `
<div>
Expand All @@ -896,7 +896,7 @@ describe('<Field />', () => {

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 () => {
Expand Down
2 changes: 1 addition & 1 deletion packages/vee-validate/tests/Form.spec.ts
Expand Up @@ -1610,7 +1610,7 @@ describe('<Form />', () => {
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 () => {
Expand Down
53 changes: 38 additions & 15 deletions packages/vee-validate/tests/useField.spec.ts
Expand Up @@ -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() {
Expand All @@ -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: `
<input name="field" v-model="value" />
<span id="meta">{{ meta.valid ? 'valid' : 'invalid' }}</span>
<button @click="resetField()">Reset</button>
<input id="input1" name="field" v-model="value1" />
<span id="meta1">{{ meta1.valid ? 'valid' : 'invalid' }}</span>
<button id="r1" @click="reset1()">Reset</button>
<input id="input2" name="field" v-model="value2" />
<span id="meta2">{{ meta2.valid ? 'valid' : 'invalid' }}</span>
<button id="r2" @click="reset2()">Reset</button>
`,
});

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 () => {
Expand Down
25 changes: 21 additions & 4 deletions packages/vee-validate/tests/useForm.spec.ts
Expand Up @@ -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';
Expand Down Expand Up @@ -284,36 +285,52 @@ describe('useForm()', () => {
});

test('resets the meta valid state on reset', async () => {
let passwordValue!: Ref<string>;
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<string>('password', val => (val ? true : REQUIRED_MESSAGE));
passwordValue = pwValue;

return {
value,
formMeta,
resetForm,
errors,
};
},
template: `
<input v-model="value" />
<span id="meta">{{ formMeta.valid ? 'valid': 'invalid' }}</span>
<span id="errors">{{ errors }}</span>
<button @click="resetForm()">Reset Meta</button>
`,
});

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 () => {
Expand Down

0 comments on commit a61f7ab

Please sign in to comment.