|
| 1 | +import { defineRule, useField } from '@/vee-validate'; |
| 2 | +import { defineComponent } from '@vue/runtime-core'; |
| 3 | +import { toRef } from 'vue'; |
1 | 4 | import * as yup from 'yup'; |
2 | 5 | import { mountWithHoc, setValue, getValue, dispatchEvent, flushPromises } from './helpers'; |
3 | 6 |
|
@@ -574,3 +577,92 @@ test('clears old errors path when item is removed when no form schema is present |
574 | 577 |
|
575 | 578 | expect(errorList.children).toHaveLength(2); |
576 | 579 | }); |
| 580 | + |
| 581 | +// #3748 |
| 582 | +test('clears old errors path when last item is removed and value update validation is on', async () => { |
| 583 | + const onSubmit = jest.fn(); |
| 584 | + defineRule('required', (v: any) => (v ? true : REQUIRED_MESSAGE)); |
| 585 | + const InputField = defineComponent({ |
| 586 | + props: { |
| 587 | + rules: { |
| 588 | + type: String, |
| 589 | + required: true, |
| 590 | + }, |
| 591 | + name: { |
| 592 | + type: String, |
| 593 | + required: true, |
| 594 | + }, |
| 595 | + label: String, |
| 596 | + type: { type: String, default: 'text' }, |
| 597 | + }, |
| 598 | + setup(props) { |
| 599 | + const { value, handleChange, errors } = useField(toRef(props, 'name'), props.rules, { |
| 600 | + label: props.label, |
| 601 | + type: props.type, |
| 602 | + }); |
| 603 | + |
| 604 | + return { |
| 605 | + value, |
| 606 | + errors, |
| 607 | + handleChange, |
| 608 | + }; |
| 609 | + }, |
| 610 | + template: ` |
| 611 | + <label :for="name">{{ label }}</label> |
| 612 | + <input :type="type" :name="name" :value="value" @input="handleChange" /> |
| 613 | + <span>{{ errors[0] }}</span> |
| 614 | + `, |
| 615 | + }); |
| 616 | + |
| 617 | + mountWithHoc({ |
| 618 | + components: { |
| 619 | + InputField, |
| 620 | + }, |
| 621 | + setup() { |
| 622 | + const initialValues = { |
| 623 | + users: ['first', 'second', 'third'], |
| 624 | + }; |
| 625 | + |
| 626 | + const schema = yup.string().required(); |
| 627 | + |
| 628 | + return { |
| 629 | + onSubmit, |
| 630 | + schema, |
| 631 | + initialValues, |
| 632 | + }; |
| 633 | + }, |
| 634 | + template: ` |
| 635 | + <VForm @submit="onSubmit" :initial-values="initialValues" v-slot="{ errors }"> |
| 636 | + <FieldArray name="users" v-slot="{ remove, push, fields }"> |
| 637 | + <fieldset v-for="(field, idx) in fields" :key="field.key"> |
| 638 | + <legend>User #{{ idx }}</legend> |
| 639 | + <label :for="'name_' + idx">Name</label> |
| 640 | + <InputField :name="'users[' + idx + ']'" rules="required" /> |
| 641 | +
|
| 642 | + <button class="remove" type="button" @click="remove(idx)">X</button> |
| 643 | + </fieldset> |
| 644 | + </FieldArray> |
| 645 | +
|
| 646 | +
|
| 647 | + <ul class="errors"> |
| 648 | + <li v-for="error in errors">{{ error }}</li> |
| 649 | + </ul> |
| 650 | +
|
| 651 | + <button class="submit" type="submit">Submit</button> |
| 652 | + </VForm> |
| 653 | + `, |
| 654 | + }); |
| 655 | + |
| 656 | + await flushPromises(); |
| 657 | + const submitBtn = document.querySelector('.submit') as HTMLButtonElement; |
| 658 | + const errorList = document.querySelector('ul') as HTMLUListElement; |
| 659 | + const removeBtnAt = (idx: number) => document.querySelectorAll('.remove')[idx] as HTMLButtonElement; // remove the second item |
| 660 | + |
| 661 | + submitBtn.click(); |
| 662 | + await flushPromises(); |
| 663 | + expect(errorList.children).toHaveLength(0); |
| 664 | + removeBtnAt(2).click(); |
| 665 | + await flushPromises(); |
| 666 | + |
| 667 | + expect(errorList.children).toHaveLength(0); |
| 668 | +}); |
0 commit comments