So I have a Vue component that is built roughly like this:
<DialogLayout>
<template #activator="slotData">
<slot name="activator" v-bind="slotData"></slot>
</template>
<template #default="{ disabled }">
<v-tabs-items v-model="openTab" transition="fade">
<v-tab-item key="notPreview">
<div></div>
</v-tab-item>
<v-tab-item key="preview" data-testid="preview-button">
<h4>{{ title }}</h4>
<Card :post="preview"
></Card>
</v-tab-item>
</v-tabs-items>
</template>
</DialogLayout>
And then the test is this:
it('Image should be visible in preview', async () => {
const component = createComponent()
await component.findComponent(DialogLayout).setData({show: true})
const previewBtn = component.find('[data-testid="preview-button"]')
expect(previewBtn.exists())
await previewBtn.trigger('click')
})
It's basically two pages inside a v-dialog, and what I want to do is switch the page and render the second page to see the image that has been uploaded, but I can't figure out how to click the v-tab-item for it to change. It doesn't seem to even be able to find the button component with the data-testid.
Does anyone have any idea on how to test clicking on a v-tabs-items in vue-test-utils? I'm not interested in testing the internal function in v-tab, but I need to be able to actually click the data-testid preview-button to see the change.
I have to find and setData for the dialog to even show, and I'm mocking the transition with:
const transitionStub = () => ({
render: function(h) {
return this.$options._renderChildren
},
})
Any help is greatly appreciated!
So one of the problems with this was that it didn't render the v-tab-item, while it should have, so instead of trying to fix the test according to the code, i instead added eager to the v-tab-item. More information can be found here.
Related
We have some complex forms and depending on the login we render different buttons.
However the component comes from a library installed via NPM. So I'm having trouble reaching them in Jest.
Is there a way to render / load the components so I can test the right ones are present?
describe('P1 buttons - show the correct buttons for credential type and registration status', () => {
beforeEach(async () => {
await wrapperBeforeEach();
});
test('expect button for OPEN_BANKING_UK_MANUAL to be visible', async () => {
currentRegistrationDetails.credentials = institutionP1UkAutoBarclays;
wrapper.vm.currentRegistrationDetails.platform = 'P1';
await wrapper.vm.$nextTick();
// wont work if Button is component
const button = wrapper.find('[data-test-id="button-p1-download-ssa"]');
// this would work if the Button component was local to the project
const buttomExternal = wrapper.findComponent(Button);
const registrationP1 = wrapper.find('.registration-p1');
console.log(registrationP1.html());
// this fails as Button doesn't render at all.
expect(buttomExternal.isVisible()).toBe(true);
});
});
The HTML from console.log(registrationP1.html()); is this —
<div class="form-actions mb-5">
<div class="form-actions-content">
<!---->
<!---->
</div>
</div>
The buttons should appear between the
<!---->
<!---->
We keep running into these issues with our applications whilst using our component library.
The actual button markup is
<Button
data-test-id="button-p1-download-ssa"
type="primary"
class="primary button"
:is-loading="savingSsa"
:on-click="downloadTheSsaFromModal"
>Request the SSA</Button
>
I think you only have to test that the props are bound correctly with the npm dependency. It's not in your scope to test that the Button from an other library is working.
I'm kinda new to Vue, Nuxt, and Vuetify and their aspects. I'm working on a Nuxt project with Vuetify and wanna use its skeleton loader but it's kinda messy. right now I use this pattern
template:
<v-skeleton-loader :loading="isLoading" type"card">
<mycomponent />
</v-skeleton-loader>
script
import skeleton from '#plugins/mixins/skeleton.js
export default {
mixins:[skeleton]
}
skeleton.js
export default{
data(){
return{
loading: null
}
},
computed:{
isLoading(){
return this.loading
}
},
created(){
this.loading = true
},
mounted(){
this.loading = false
}
}
when I first used it it was working perfectly. i had a static page and each of its components had their own skeleton and every time i loaded the page it would show their skeleton until they were loaded.
BUT.... as I started using this pattern on different pages i found out that it has many flaws!!
it only shows the skeleton when the page is refreshed!
won't show when I add components or data to the page! for example, an Axios call to get the product
it won't work when changing between routes
and so on ...
So, my question is, What's the best and most practical way to use the skeleton loader! i had a page with a v-for loop through a component and the component had its own skeleton in its template. it only show skeleton on refresh!
like this:
<div v-for="i in 10" :key="i">
<mycomp />
</div>
mycomp:
<v-skeleton-loader :loading="isLoading" type"card">
// components html codes
</v-skeleton-loader>
I would you suggest to create skeleton component. And in main component most of apps do this stuff, where the amount of skeleton is fixed or limited by pagination:
<template v-if="loading">
<skeleton v-for="i in 10" :key="i" />
</template>
<template v-else>
<div v-for="item in items" :key="item.id">
{{ item }}
</div>
</template>
Is there a way to display a Vuetify loader component until all the elements are loaded? If not it displays for a moment the {{ and }} and is not very aesthetic.
I have a big page and a lot of items, applying v-cloak on each item don't consider to be cool either, I prefer to display a loader.
The Vuetify overlay component is not a bad choice in this kind of instance. You can chose an opaque overlay with an indeterminate loader that you then hide when everything has loaded:
<template>
<div>
<div id="the-content">
This is the stuff that gets hidden until it's loaded...
</div>
<v-overlay
:opacity="1"
:value="overlay"
>
<v-progress-circular indeterminate size="64">
Loading...
</v-progress-circular>
</v-overlay>
</div>
</template>
<script>
export default {
data: () => ({
overlay: true,
}),
mounted() {
// hide the overlay when everything has loaded
// you could choose some other event, e.g. if you're loading
// data asynchronously, you could wait until that process returns
this.overlay = false
},
}
</script>
Note: in this example, you're not likely to see the loading overlay since the content on this page takes such a short time to actually load.
I am using vuex store state to show/hide Vuetify v-dialog in my NuxtJS app. Following are the code excerpt:
Vuex Store:
export const state = () => ({
dialogOpen: false
});
export const mutations = {
setDialogToOpen(state) {
state.dialogOpen = true;
},
setDialogToClosed(state) {
state.dialogOpen = false;
}
};
export const getters = {
isDialogOpen: state => {
return state.dialogOpen;
}
};
Dialog Component:
<v-dialog
v-model="isDialogOpen"
#input="setDialogToClosed"
max-width="600px"
class="pa-0 ma-0"
>
...
</v-dialog>
computed: {
...mapGetters("store", ["isDialogOpen"])
},
methods: {
...mapMutations({
setDialogToClosed: "store/setDialogToClosed"
})
}
This all works fine but when I redirect from one page to another page like below it stops working.
this.$router.push("/videos/" + id);
I hit browser refresh and it starts working again. Using the Chrome Vue dev tools, I can see the state is set correctly in the store as well as in the v-dialog value property as shown below
In Vuex store
In v-dialog component property
Yet the dialog is not visible. Any clue what is happening?
I am using NuxtJS 2.10.2 and #nuxtJS/Vuetify plugin 1.9.0
Issue was due to v-dialog not being wrapped inside v-app
My code was structured like this
default layout
<template>
<div>
<v-dialog
v-model="isDialogOpen"
#input="setDialogToClosed"
max-width="600px"
class="pa-0 ma-0"
>
<nuxt />
</div>
</template>
Below is the code for index page which replaces nuxt tag above at runtime.
<template>
<v-app>
<v-content>
...
</v-content>
</v-app>
</template>
So, in the final code v-dialog was not wrapped inside v-app. Moving v-app tag to default layout fixed it
<template>
<v-app>
<v-dialog
v-model="isDialogOpen"
#input="setDialogToClosed"
max-width="600px"
class="pa-0 ma-0"
>
<nuxt />
</v-app>
</template>
I have a list of items that don't get created until after an async call happens. I need to be able to get the getBoundingClientRect() of the first (or any) of the created items.
Take this code for instance:
<template>
<div v-if="loaded">
<div ref="myItems">
<div v-for="item in items">
<div>{{ item.name }}</div>
</div>
</div>
</div>
<div v-else>
Loading...
</div>
</template>
<script>
import axios from 'axios';
export default {
data() {
return {
items: []
}
},
created() {
axios.get('/url/with/some/data.json').then((response) => {
this.items = response.data;
this.loaded = true;
}, (error) => {
console.log('unable to load items');
});
},
mounted() {
// $refs is empty here
console.log(this.$refs);
// this.$refs.myItems is undefined
}
};
</script>
So, I'm trying to access the myItems ref in the mounted() method, but the this.$refs is empty {} at this point. So, therefore, I tried using a component watch, and various other methods to determine when I can read the ref value, but have been unsuccessful.
Anyone able to lead me in the right direction?
As always, thanks again!!
UPDATES
Added a this.$watch in the mounted() method and the $refs still come back as {}. I then added the updated() method to the code, then was able to access $refs there and it seemed to work. But, I don't know if this is the correct solution?
How does vuejs normally handle something like dynamically moving a div to an on-screen position based on async data? This is similar to what I'm trying to do, grab an element on screen once it has been rendered first (if it even should be rendered at all based on the async data), then access it to do something with it (move it to a position)?
Instead of doing on this.$refs.myItems during mounted, you can do it after the axios promise returns the the response.
you also update items and loaded, sou if you want to use watch, you can use those
A little late, maybe it helps someone.
The problem is, you're using v-if, which means the element with ref="myItems" doesn't exist yet. In your code this only happens when Axios resolves i.e. this.loaded.
A better approach would be to use a v-show.
<template>
<div>
<div v-show="loaded">
<div ref="myItems">
<div v-if="loaded">
<div v-for="item in items">
<div>{{ item.name }}</div>
</div>
</div>
</div>
</div>
<div v-show="!loaded">
Loading...
</div>
</div>
</template>
The difference is that an element with v-show will always be rendered and remain in the DOM; v-show only toggles the display CSS property of the element.
https://v2.vuejs.org/v2/guide/conditional.html#v-show