Test a form which used veevalidate on that form - vue.js

i using vee-validate version 3.0.11 for validate my form like below
<validation-observer v-slot="{ invalid }" slim>
<validation-provider rules="required" v-slot="{ errors, dirty, invalid}" slim>
<div class="form-group">
<label class="sr-only" for="txtUsername"></label>
<input
autocomplete="off"
id="txtUsername"
name="username"
type="text"
class="form-control txtUsername"
placeholder="Email or Username"
v-model="username"
v-bind:class="{ 'is-invalid': invalid && dirty,'is-valid': !invalid }" />
</div>
</validation-provider>
<validation-provider rules="required" v-slot="{ errors, dirty, invalid}" slim>
<div class="form-group">
<label class="sr-only" for="txtPassword"></label>
<input
id="txtPassword"
name="password"
type="password"
class="form-control"
placeholder="Password"
v-model="password"
v-bind:class="{ 'is-invalid': invalid && dirty,'is-valid': !invalid }" />
</div>
</validation-provider>
<div >
<button
type="button"
name="login"
class="btn btn-primary"
v-on:click="doLogin()"
:disabled="invalid">
Login
</button>
</div>
</validation-observer>
and i wrote some test with chai and mocha
in my test i need to find the button
but when i using find method for find button all html tag between validation-observer tag is not loaded in my wrapper.
my test code is:
// i change it to shallowMount to mount but problem is exist,
// mount does not render any thing between validation-observer tag
const wrapper = mount(LoginView, { sync: false });
describe('Login.vue', () => {
it('some text, () => {
console.log(wrapper.html());
// my log include all of tag except tags between the validation-observer tag
});
});
can some one tell me how i can find my button by using warraper.find(), please?

You are trying to shallow mount a component that needs to be mounted in order to render its children. If you want to ignore ValidationProvider all together you can provide a fake one as shown below.
ContactForm.vue
<template>
<div>
<ValidationProvider rules="required" name="input" v-slot="{ errors }">
<p :style="{color: 'red'}">To be, or not to be</p>
<input type="text" v-model="value">
<span id="error">{{ errors[0] }}</span>
</ValidationProvider>
</div>
</template>
<script>
import { ValidationProvider } from "vee-validate";
export default {
name: "ContactForm",
components: {
ValidationProvider
},
data: () => ({
value: ""
})
};
</script>
ContactForm.test.js
import { shallowMount, createLocalVue } from "#vue/test-utils";
import ContactForm from "./ContactForm";
import FakeValidationProvider from "./FakeValidationProvider";
test("Test shallow mount renders what's inside validation provider", async () => {
const localVue = createLocalVue();
var wrapper = shallowMount(ContactForm, {
stubs: {
ValidationProvider: FakeValidationProvider
},
localVue
});
expect(wrapper.text()).toContain("To be, or not to be");
});
FakeValidationProvider.vue
<template>
<div v-bind="{ ...$props, ...$attrs }">
<slot :errors="errors"></slot>
</div>
</template>
<script>
export default {
name: "FakeValidationProvider",
data() {
return {
errors: []
};
}
};
</script>
Feel free to extend the slot with any other parameters you need besides errors. If you want to make those parameters dynamic as well, check out this article on rendering slots

Related

Vue 3 v-model not working with custom input component in Nuxt.js

I am trying to use a custom Vue input component with a v-model, but the value is not updating in the parent component. I am using Vue 3 with Nuxt.js.
Here is my custom input component:
<template>
<input
type="text"
:value="modelValue"
#input="$emit('update:modelValue', $event.target.value)"
:placeholder="placeholder"
class="border border-gray-300 rounded-lg w-full p-2 text-black m-1"
/>
</template>
<script setup>
const props = defineProps({
modelValue: String,
placeholder: String,
});
const emit = defineEmits(["update:modelValue"]);
</script>
<script>
export default {
name: "MyInput",
};
</script>
And here is how I am using it in the parent component:
<template>
<div>
<MyInput v-model="inputValue" placeholder="Enter a value" />
</div>
</template>
<script>
import MyInput from "./MyInput.vue";
export default {
name: "MyParentComponent",
components: {
MyInput,
},
data() {
return {
inputValue: "",
};
},
};
</script>
The inputValue data property is not being updated when I type in the input field. Can someone help me figure out what I'm doing wrong?
I have a Vue 3 project without Nuxt.js using this exact same code and it works there.
there is no mistake in your codes I used exact same code and it's working with no problem
but:
there is no need to import components (Nuxt supports auto import)
your file's structure should be like this:
|__components
|
|__MyInput.vue
|
|__MyParentComponent.vue
|__app.vue
MyInput.vue
<template>
<input
type="text"
:value="modelValue"
#input="$emit('update:modelValue', $event.target.value)"
:placeholder="placeholder"
class="border border-gray-300 rounded-lg w-full p-2 text-black m-1"
/>
</template>
<script setup>
const props = defineProps({
modelValue: String,
placeholder: String,
});
const emit = defineEmits(["update:modelValue"]);
</script>
the dev tools will show the name of the component. So no additional naming is necessary.
MyComponent.vue
<template>
<div>
<MyInput v-model="inputValue" placeholder="Enter a value" />
<p>{{ inputValue }}</p>
</div>
</template>
<script setup>
let inputValue = ref("");
</script>
app.vue
<template>
<MyComponent />
</template>

vee-validate model-less validation on submit not working

this seems to be simple one but I can not get it work...
I want to validate if file is set only when I click the validate button. but the validation result in check method always return false.
<template>
<ValidationObserver>
<form #submit.prevent>
<ValidationProvider
ref="aProvider"
name="file"
rules="required"
v-slot="{ errors }"
>
<input type="file" />
<p>{{ errors[0] }}</p>
</ValidationProvider>
<button #click="check">validate!</button>
</form>
</ValidationObserver>
</template>
<script>
export default {
methods: {
async check() {
const { valid } = await this.$refs.aProvider.validate();
if (valid) {
alert("Form has been submitted!");
}
},
},
};
</script>
codesandbox https://codesandbox.io/s/codesandbox-forked-6o7iyt?file=/src/Demo.vue
The validate() method is for Observers, not for providers as specified in docs: https://vee-validate.logaretm.com/v2/guide/components/validation-observer.html#methods
Just set a ref on your Observer and run the method.
<template>
<ValidationObserver ref="form">
<form #submit.prevent>
<ValidationProvider
name="file"
rules="required"
v-slot="{ errors }"
>
<input type="file" />
<p>{{ errors[0] }}</p>
</ValidationProvider>
<button #click="check">validate!</button>
</form>
</ValidationObserver>
</template>
<script>
export default {
methods: {
async check() {
const { valid } = await this.$refs.form.validate();
if (valid) {
alert("Form has been submitted!");
}
},
},
};
</script>
Create a data property and bind it to the input via v-model. Move your ref to the ValidationObserver. Then it will validate properly.

Vue.js BootstrapVue : Veevalidate cannot validate datepicker

In my Laravel+Vue.js SPA ( Single Page Application) I am using the datepicker package from here, BootstrapVue from here and Veevalidate from here .
I think I need to show only the code inside my component file as only the datepicker component causes the problem and not other ones. My code follows in EditProfile.vue:
<ValidationObserver ref="form" v-slot="{ passes }">
<div id="registration_form">
<b-form #submit.prevent="passes(onSubmit)" #reset="resetForm">
<ValidationProvider vid="name" rules="required|min:2" name="name" v-slot="{ valid, errors }">
<b-form-group
label="User Name:"
label-for="exampleInput1"
>
<b-form-input
type="text"
v-model="name"
:state="errors[0] ? false : (valid ? true : null)"
placeholder="Enter your name"
></b-form-input>
<b-form-invalid-feedback id="inputLiveFeedback">{{ errors[0] }}</b-form-invalid-feedback>
</b-form-group>
</ValidationProvider>
<ValidationProvider vid="dob" rules="required" name="dob" v-slot="{ valid, errors }">
<b-form-group
label="Date of Birth:"
label-for="exampleInput1"
>
<datepicker
type="text"
v-model="dob"
required
format="d-M-yyyy"
:state="errors[0] ? false : (valid ? true : null)"
>
</datepicker>
<b-form-invalid-feedback id="inputLiveFeedback">{{ errors[0] }}</b-form-invalid-feedback>
</b-form-group>
</ValidationProvider>
<b-button type="submit" variant="primary">Submit</b-button>
<b-button type="reset" variant="danger">Reset</b-button>
</b-form>
</div><!-- end of id registration_form-->
</ValidationObserver>
JS part inside EditProfile.vue is:
import Datepicker from 'vuejs-datepicker';
import { ValidationObserver, ValidationProvider } from "vee-validate";
export default {
name: "EditProfile",
components: {
ValidationObserver,
ValidationProvider,
Datepicker
},
data: () => ({
name: "",
dob:""
}),
methods: {
onSubmit() {
console.log("Form submitted yay!");
},
resetForm() {
this.name = "";
this.dob = "";
requestAnimationFrame(() => {
this.$refs.form.reset();
});
}
}
};
When the submit button is pressed then validation works for name field but no error message shows up when dob field on datepicker is empty .
My Vue.js version is 2.6.10 and I used the latest versions of BootstrapVue and Veevalidate (3) as well.
How can I make the validation work for the date picker as well ?

Vue + Vee Validate 3 Trigger Manual Validation

I've almost got my form validation working - I'm using Vee Validate 3 with Vue.js. Most of the examples of Vee Validate are for version 2, so I'm struggling a bit.
The issue I have is triggering the validation when submitting the form.
If I click the text field to focus on it first, then click submit, the validation fires and I see the error message.
If however, I don't click the text field first and just click the submit button, I don't see the error message.
How can I make this work without having to focus on the text field before I click submit?
Update
Weirdly, the console shows the error TypeError: this.validate is not a function in both cases - whether the validation works or not.
<ValidationProvider rules="required" v-slot="{ validate, errors }">
<div>
<input type="text" rules="required">
<p id="error">{{ errors[0] }}</p>
</div>
</ValidationProvider>
<script>
export default {
methods: {
async validateField() {
const valid = await this.validate()
}
}
};
</script>
Adam pointed me in the right direction with the ValidationObserver.
This code works for me...
<ValidationObserver ref="observer" v-slot="{ invalid }" tag="form" #submit.prevent="submit()">
<ValidationProvider rules="required" v-slot="{ errors }">
<input type="text">
<p id="error">{{ errors[0] }}</p>
</ValidationProvider>
<button #click="submit()">Submit>/button>
</ValidationObserver>
<script>
import { VslidationProvider, ValidationObserver } from 'vee-validate'
import { required } from 'vee-validate/dist/rules'
export default {
methods: {
async submit () {
const valid = await this.$refs.observer.validate()
}
}
};
</script>
New way of doing it
<ValidationObserver v-slot="{ handleSubmit }">
<ValidationProvider rules="required" v-slot="{ errors }">
<input type="text">
<p id="error">{{ errors[0] }}</p>
</ValidationProvider>
<button #click="handleSubmit(onSubmit)">Submit>/button>
</ValidationObserver>
<script>
import { VslidationProvider, ValidationObserver } from 'vee-validate'
export default {
methods: {
onSubmit() {
// ...
}
}
};
</script>

How can I get value in datetimepicker bootstrap on vue component?

My view blade, you can see this below :
...
<div class="panel-body">
<order-view v-cloak>
<input slot="from-date" data-date-format="DD-MM-YYYY" title="DD-MM-YYYY" type="text" class="form-control" placeholder="Date" name="from_date" id="datetimepicker" required>
<input slot="to-date" data-date-format="DD-MM-YYYY" title="DD-MM-YYYY" type="text" class="form-control" placeholder="Date" name="to_date" id="datetimepicker" required>
</order-view>
</div>
...
My order-view component, you can see this below :
<template>
<div>
<div class="col-sm-2">
<div class="form-group">
<slot name="from-date" required v-model="fromDate"></slot>
</div>
</div>
<div class="col-sm-1">
<div class="form-group" style="text-align: center">
-
</div>
</div>
<div class="col-sm-2">
<div class="form-group">
<slot name="to-date" required v-model="toDate"></slot>
</div>
</div>
<div class="col-sm-4">
<button v-on:click="filter()" class="btn btn-default" type="button">
<span class="glyphicon glyphicon-search"></span>
</button>
</div>
</div>
</template>
<script>
export default {
data() {
return{
fromDate: '',
toDate: ''
}
},
methods: {
filter: function() {
console.log(this.fromDate)
console.log(this.toDate)
}
}
}
</script>
I using v-model like above code
But, when I click the button, the result of
console.log(this.fromDate)
console.log(this.toDate)
is empty
It display empty
Why it does not work?
How can I solve it?
You cannot bind a slot using v-model and expect that Vue will attach that automatically to your slot input, but I can't see any reason why you need to use a slot here anyway. It looks like you just want an input that you can attach custom attributes to and you can do that by passing the attributes as a prop and use v-bind to bind them:
<template>
<div>
<input v-bind="attrs" v-model="fromDate" />
<button #click="filter">filter</button>
</div>
</template>
export default{
props: ['attrs'],
methods: {
filter() {
console.log(this.fromDate)
}
},
data() {
return {
fromDate: ""
}
}
}
new Vue({
el: "#app",
data: {
fromDateAttrs: {
'data-date-format': "DD-MM-YYYY",
title: "DD-MM-YYYY",
type: "text",
class: "form-control",
placeholder: "Date",
name: "from_date",
id: "datetimepicker",
}
}
});
Now you can just pass your attrs as a prop in the parent:
<my-comp :attrs="fromDateAttrs"></my-comp>
Here's the JSFiddle: https://jsfiddle.net/rvederzc/
EDIT
In reference as to how to create a date picker component, here's how I would implement a jQuery datepicker using Vue.js:
<template id="date-picker">
<div>
<input v-bind="attrs" v-model="date" #input="$emit('input', $event.target.value)" v-date-picker/>
</div>
</template>
<script type="text/javascript">
export default {
props: ['attrs'],
directives: {
datePicker: {
bind(el, binding, vnode) {
$(el).datepicker({
onSelect: function(val) {
// directive talk for 'this.$emit'
vnode.context.$emit('input', val);
}
});
}
}
}
}
</script>
You can then bind that with v-model in the parent:
<date-picker v-model="myDate"></date-picker>
Here's the JSFiddle: https://jsfiddle.net/g64drpg6/
Cant expect any javascript technology to be complete before it becomes famous. Going by that, I tried all the recommendations from using moment to vue-datapicker. All recommendations heavily broke design and needed hardcode of the div id's in the vue initialisation under mounted. Cant introduce hacks into my project this way. Messes up design and implementation neatness.
I fixed it using plain old jsp. On Save, I just did this
vuedata.dateOfBirthMilliSecs = $("#dateOfBirth").val() ;
I'll figure out conversion of date format to milliseconds in my java controller.