I have been instructed to assert that our errors have been surfaced in bootstrap-vue toasts. How can I assert that the toast has been made on my tests. I was hoping to mock out ‘this.$bvToast.toast’ however I have been unable to find anything salient so far.
I stumbled upon the same problem and the way I solved it was to wrap the this.$bvToast.toast() around a component method showToast():
// component
{
...
showToast(title: string, content: string) {
this.$bvToast.toast(
content,
{
title,
...
}
}
}
}
Then, in my jest file I just use a spy and make sure the parameters are the ones I expect
// component.spec
const mySpy = jest.spyOn(myComponent, 'showToast');
...
your code here
...
expect(mySpy).toHaveBeenCalledWith(expectedTitle, expectedContent)
Of course, this doesn't test the actual text inside the bootstrap-vue toast component, but this defeats also the purpose of the unit test: you assume that the toast works as it should :)
Related
I wanted to try something for performance/convenience purposes, I understand the gains will be minimal but understanding how/if/why this works would also just be helpful to learn.
I have a some custom data types (defined as classes) that are used to identify certain properties throughout my application. I want to use a static function on the type to define a display function. (stripped down) Example:
class Email extends String{
static display = (value) => {
return `<a href='mailto${value}'>${value}</a>`;
}
}
Call it like you do:
Email.display("test#test.com");
And that works in the template, so long as it’s in a v-html attribute. This is perfectly acceptable.
It’s probably important to specify I’m working with Vue-CLI and single-file components, so all that sweet hyperscript gets created at compile time.
But it got me thinking, is there a way I can pass a freshly-created hyperscript to the template at render? Preferably in a way that works in the {{mustache}} if at all possible.
I tried doing it with h but that just displays the ol’ [object Object].
class Email extends String{
static display = (value) => {
return h('a', {innerHtml: value});
}
}
Update: also tried
I thought maybe going around the Vue render functions could get the job done, but they don't seem to like document fragments either.
static display = (value) => {
var fragment = document.createDocumentFragment();
var a = document.createElement('a');
a.textContent = value;
fragment.appendChild(a);
return fragment;
}
Question
Is there a way create hyperscript at runtime and utilize it in a vue template? Bonus points if it works in {{mustache}} and v-html.
Generally component templates and render functions that use JSX or h (hyperscript) are mutually exclusive.
It's really possible to do this, in this case display is actually functional component, and it needs to be output as any other dynamic component:
setup() {
const display = (props) => {
return h(...);
};
return { display };
}
and
<component :is="display" :value="..."/>
The return of display is a hierarchy of vnode objects, they can't be used as is in v-html without being previously rendered to HTML.
I have gone through the composition api docs for vee-validate, and I can definitely get my validation working on my forms if I follow the pattern described in their docs.
I don't feel comfortable however doing it as documented there, as I just feel I'm writing too much code for each form, and I do not want to repeat myself.
So I've been experimeting a bit with how I can optimise this, and this is what we currently came up with, but I'm a bit stuck now.
You can see the code example on https://codesandbox.io/s/restless-star-1qwgb, but I'll walk you through.
Consider we want to create a form for creating a new Invoice.
An invoice is typically composed of the Invoice model with a number of InvoiceElements attached to it. (the invoice lines)
In our vueJs codebase, we have javascript classes representing each model that we need to work with, so you'll find a class Invoice and a class InvoiceElement, both extending BaseModel which already providers some basic functionality.
On each mode, we have defined a static method returning a yup validation schema, e.g.:
import * as yup from "yup";
import BaseModel from "./BaseModel";
import InvoiceElement from "./InvoiceElement";
export default class Invoice extends BaseModel {
static get validationSchema() {
return yup.object().shape({
due_date: yup.date().min(new Date()).default("2021-09-30"),
reference: yup.string(),
elements: yup
.array()
.of(InvoiceElement.validationSchema)
.default([InvoiceElement.validationSchema.getDefault()])
});
}
}
As you can see, we also define default values for each of the schema fields.
This allows us to do the following in our form component:
setup() {
let { handleSubmit, errors, values } = useForm({
validationSchema: Invoice.validationSchema,
initialValues: Invoice.validationSchema.getDefault(),
validateOnMount: false,
});
let addLine = () => {
values.elements.push(InvoiceElement.validationSchema.getDefault());
};
let submitForm = handleSubmit((values) => {
alert("form was valid and we submit data here");
});
return {
values,
errors,
addLine,
submitForm,
};
}
Okay - I'm very happy with this as we now have:
values which is reactive and I can just bind to my inputs using ```
errors which has my errors
When I submit the form, it correctly triggers vee-validate's handleSubmit() method and my errors get correctly updated.
My main issue with this approach now is how to trigger the validation of the fields when they get updated. The main goal is to avoid having to write too much code using the useField() composable.
I know I'm not following the proposed pattern, but it just kept feeling as if we were writing too much code, and we seem quite close to a good pattern, but I just don't get the last bits...
Maybe someone on here does though :-)
Generally, the docs are only meant to show you "how it works", but I do agree that useField is very verbose if used incorrectly. Using it for models isn't the intended use-case.
For example:
// Very verbose and not the intended use-case
const { value: email } = useField(...);
const { value: password } = useField(...);
The main purpose of useField is to help create input components that can be validated, that if you are willing to couple your form components with vee-validate which I find reasonable in 80% of situations.
To try to answer your issue, since you want to avoid using useField you could actually make use of the other useXXX functions called composition helpers. Like useValidateField which creates a function for you that can validate any field given it was created in a child of a useForm component.
const validate = useValidateField('fieldName');
validate(); // triggers validation on the field
Say you have a vue component with a method like this:
methods:{
doSomething(someParameter){
//maybe do something with that Parameter
this.$store.commit("storeSomething",someParameter);
let someParameter2 = this.transformToSth(someParameter);
this.$store.commit("storeSomethingElse",someParameter2);
}
}
What do I have to do, so that this kind of test works in Jest?
test("that the commit was correctly called",()=>{
wrapper.vm.doSomething(someParameter);
expect(wrapper.vm.$store.commit).hasBeenCalledWith(someParameter);
expect(wrapper.vm.$store.commit).hasBeenCalledWith(someParameter2);
})
Also note that I want that the mock is also reverted again, so that method uses the same implementation as before. Otherwise, I create a dependency between tests, which I very much like to avoid.
(I also do hope it works the same way with actions and getter Functions)
So I found that one can solve this with spyOn:
test("that the commit was correctly called", () => {
let spy = jest.spyOn(userCardEditingWrapper.vm.$store, "commit");
wrapper.vm.doSomething("test");
expect(spy).toHaveBeenCalledWith("storeSomething", someParameter);
expect(spy).toHaveBeenCalledWith("storeSomethingElse", someParameter2);
});
Credit to #devTea from the Vue Discord Channel that gave me the hint with jest.fn().
Creating a web component using svelte, I need to run some code when an attribute of the component is changed.
What I have come up with is the following pattern.
<script>
export let test = "default value";
$: {
testIsChanged(test);
}
function testIsChanged(newValue) {
console.log(newValue);
}
</script>
The value of test is {test}
Is this the way to do it?
Or am I missing something?
That will indeed work, as you can see in this REPL
I am trying to use jQuery Lazy Loading in my Laravel/Vue project but I am struggling to get an image to appear in my Vue component. I have the following img block which I thought would work:
<img v-if="vehicle.photo_path != null" :data-original="'/storage/vehicles/' + vehicle.photo_path" class="lazy" height="180" width="150"/>
I did find this other question on here - Static image src in Vue.js template - however when I try that method I get this: Interpolation inside attributes has been removed. Use v-bind or the colon shorthand instead.
So I switched back to the v-bind method but all I am getting is a white box with a grey border - no image. If I v-bind on the src attribute however I can see the image correctly.
I know I have implemented the Lazy Loading plugin correctly as I can successfully call it elsewhere on my site (such as on a blade view), but I'm not sure where I am going wrong. Thank you.
Try moving the call to $("img.lazy").lazyload() into the mounted() method on your Vue instance. I just had a similar issue with Vue and jQuery Lazyload and that solved it for me. For example:
var app = new Vue({
el: ...
data() ...
computed: ...
methods: ...
mounted() {
$("img.lazy").lazyload()
}
})
I found many modules on the internet, but I like to avoid modules when I can. So I came up with something else, I don't know if it's the best, but it works and I didn't see any performances loss, I lazy load all the images when the page loads. You may prefer on scroll if you have lots of them, but I guess you'll figure this out if my answer fits your needs.
You'll need vueX for this, but I'll avoid the set up as this is not replying to your question.
If, like me, you have some kind of Index.vue component which main usage is to initiate all the child components (I use to do that for vue-router for instance), place a mounted() function in it :
mounted(){
const ctx = this;
// You could use this method, but if you reload the page, the cache of the browser won't allow your JS to trigger .onload method, so better use what's after.
/*window.onload = function(){
console.log('page loaded, can load images');
ctx.$store.dispatch('setPageLoaded', true);
}*/
let interval = setInterval(function() {
if(document.readyState === 'complete') {
clearInterval(interval);
ctx.$store.dispatch('setPageLoaded', true);
}
}, 100);
}
=> On the page load, I just set a page_load variable in the store to true.
Then, in any component you'd like to lazy load the image, just use 2 computeds (I used a mixin that I include in my components so I avoid repeating some code) :
computed: {
page_loaded(){
return this.$store.getters.getPageLoaded;
},
image(){
if(this.page_loaded){
console.log('starting loading image');
if(this.product.picture){
return resizedPicture(this.product.picture, this.width, this.height);
}else if(this.product.hasOwnProperty('additional_pictures') && this.product.additional_pictures.length){
return resizedPicture(this.product.additional_pictures[0], this.width, this.height);
}
return '';
}
}
}
page_loaded() goal is to return the store page_loaded variable I talk about.
image() goal is to actually load the image.
I used that for inline CSS background-image property, so my HTML looks like this :
<span v-if="page_loaded" class="image" :style="'background-image:url('+image+')'" :width="1200" :height="900"></span>
I hope it'll help, but as I said, feel free guys to tell me if it's not optimized.