How to unit test a variable in vue and vuetify with jest? - vue.js

This might be pretty simple.
I have a component that renders a receive a message via props and renders it in a v-card-text. It doesn't do that in an optimal way, the whole v-if is messy, but it works.
<template>
<!-- Needed to be rendered big -->
<div v-if="start == true" class="ma-0">
<v-card class="text-xs-right" dark color="rgb(0,140,69)" height="50" width="1060" >
<v-btn dark text icon #click= "isOpen = !isOpen;" color="rgb(0,140,69)" >
<v-icon dark>fullscreen</v-icon>
</v-btn>
<v-btn class="mx-2" icon dark small color="rgb(0,140,69)">
<v-icon dark>thumb_up</v-icon>
</v-btn>
</v-card>
<v-card v-model='text' color="" height="200" width="1060">
<v-card-text ref="tcardbig" color="rgb(0,140,69)" style="color:rgb(0,140,69);display:flex;height:100%;justify-content:center;" class="display-2 font-weight-black align-center"> {{text}}</v-card-text>
</v-card>
</div>
<!-- Needed to be rendered small -->
<div v-else class="ma-2">
<v-card class="text-xs-right" dark color="rgb(0,140,69)" height="50" width="500" >
<v-btn dark text icon #click= "isOpen = !isOpen;" color="rgb(0,140,69)" >
<v-icon dark>fullscreen</v-icon>
</v-btn>
<v-btn class="mx-2" icon dark small color="rgb(0,140,69)">
<v-icon dark>thumb_up</v-icon>
</v-btn>
</v-card>
<v-card v-model='text' color="" height="350" width="500">
<v-card-text ref="tcardsmall" color="rgb(0,140,69)" style="color:rgb(0,140,69);display:flex;height:100%;justify-content:center;" class="display-2 font-weight-black align-center"> {{text}}</v-card-text>
</v-card>
</div>
</template>
<script>
export default {
data(){
return {
text: 'lorem ipsum',
start: true
}
},
props: ['textProps', 'startProps'],
mounted(){
this.text = this.textProps
this.start = this.startProps
}
}
</script>
I'm trying to unit test it with jest. How do I check if the text is properly rendered?
In other how I check the {{text}} in the OR how I check if the text variable of the component has the value I sent via prop. What am I doing wrong?
import {createLocalVue, mount} from '#vue/test-utils'
import textCard from './textCard.vue'
import Vue from 'vue'
import Vuetify from 'vuetify'
Vue.use(Vuetify)
const localVue = createLocalVue()
describe('textCard.vue', () => {
const wrapper = mount(textCard,
{
localVue,
vuetify,
propsData: {
text: 'Random Text',
start: false
},
}
)
it('renders a vue instance', () => {
expect(mount(textCard).isVueInstance()).toBe(true);
});
const content = wrapper.find({ ref: 'tcardsmall' })
it('Checks the if the variable text is correct', () => {
expect(content.selector).toMatch('Random Text')
})
}
)

Related

Vue transition breaking layout while transitioning

I am facing a problem where, when i close the search text field (nav-search component), the layout breaks for a very short amount of time (less than a second). I think it is caused by the fact that the searchClosed property is updated to false before the animation ends, so the UI content that should be hidden appears before the search text field has fully disapeared.
How could i fix this behavior? Am i doing something wrong? I have no clue how to make :class="{'d-flex justify-space-around' : searchClosed, 'hidden': !searchClosed} sync with the transition effect.
Here is my block of code:
<template>
<v-app dark>
<v-app-bar fixed app class="app-bar">
<v-row>
<v-col cols="2">
<v-app-bar-nav-icon
aria-label="show-or-hide-navigation-menu"
#click.stop="drawer = !drawer"
/>
</v-col>
<v-col :class="{'d-flex justify-space-around' : searchClosed, 'hidden': !searchClosed}">
<nuxt-link aria-label="home-page" to="/" class="d-flex">
<v-img
:src="require('~/assets/images/example_Logo.svg')"
max-height="55px"
max-width="110px"
class="mb-1"
contain
></v-img>
</nuxt-link>
</v-col>
<v-col cols="2" :class="{'d-flex justify-end' : searchClosed, 'hidden' : !searchClosed}">
<v-btn aria-label="show-user-menu" icon>
<v-icon>mdi-account-circle</v-icon>
</v-btn>
<v-btn
aria-label="show-or-hide-search-input"
icon
#click="searchClosed = false"
>
<v-icon>mdi-magnify</v-icon>
</v-btn>
</v-col>
<transition name="slide-fade">
<nav-search
v-show="!searchClosed"
#search-opened="searchClosed = false"
#search-closed="searchClosed = true"
></nav-search>
</transition>
</v-row>
</v-app-bar>
<v-navigation-drawer v-model="drawer" temporary fixed app>
<v-btn #click="handleDarkMode">Toggle Dark</v-btn>
<template v-if="loggedIn === undefined">
<nuxt-link to="/register">
<v-btn>Register</v-btn>
</nuxt-link>
<nuxt-link to="/login">
<v-btn>login</v-btn>
</nuxt-link>
</template>
<template v-else>
<v-btn #click="logout">Logout</v-btn>
</template>
</v-navigation-drawer>
<v-main>
<nav-search-results-list v-if="displaySearchResults" />
<Nuxt #click.native=";(searchClosed = true), (searchResults = false)" />
</v-main>
<v-footer :absolute="!fixed" app>
<span>© {{ new Date().getFullYear() }}</span>
</v-footer>
</v-app>
</template>
<script>
import NavSearch from '~/components/NavSearch.vue'
export default {
name: 'MainLayoutComponent',
components: { NavSearch },
data() {
return {
searchClosed: true,
drawer: false,
fixed: false,
title: 'exampleApp',
}
},
computed: {
displaySearchResults() {
return this.$store.state.search.displaySearchResults
},
loggedIn() {
return this.$auth.$storage.getState('user')
},
},
methods: {
handleDarkMode() {
this.$vuetify.theme.dark = !this.$vuetify.theme.dark
},
async logout() {
await this.$auth.logout('local')
this.$auth.$storage.removeUniversal('user')
},
},
}
</script>
<style scoped>
.slide-fade-enter-active {
transition: all 0.9s ease;
}
.slide-fade-enter,
.slide-fade-leave-to {
transform: translateX(10px);
opacity: 0;
}
.app-bar >>> .v-toolbar__content {
padding: 2px !important;
}
.hidden{
display: none;
}
</style>

reset a vuetify stepper

I'm looking for a function who can resetting my stepper made with vuetify.
the e1 is set as 0 but if I make a function who reset this value to 0, it doesn't work and the stepper set as the same screen.
It is possible to reset a stepper to default state
Find the working codepen here: https://codepen.io/chansv/pen/wvvzddP?editors=1010
<div id="app">
<v-app id="inspire">
<v-stepper v-model="step" vertical>
<v-stepper-header>
<v-stepper-step step="1" :complete="step > 1">Your Information</v-stepper-step>
<v-divider></v-divider>
<v-stepper-step step="2" :complete="step > 2">Your Address</v-stepper-step>
<v-divider></v-divider>
<v-stepper-step step="3">Misc Info</v-stepper-step>
</v-stepper-header>
<v-stepper-items>
<v-stepper-content step="1">
<v-text-field label="Name" v-model="registration.name" required></v-text-field>
<v-text-field label="Email" v-model="registration.email" required></v-text-field>
<v-btn color="primary" #click.native="step = 2">Continue</v-btn>
</v-stepper-content>
<v-stepper-content step="2">
<v-text-field label="Street" v-model="registration.street" required></v-text-field>
<v-text-field label="City" v-model="registration.city" required></v-text-field>
<v-text-field label="State" v-model="registration.state" required></v-text-field>
<v-btn flat #click.native="step = 1">Previous</v-btn>
<v-btn color="primary" #click.native="step = 3">Continue</v-btn>
</v-stepper-content>
<v-stepper-content step="3">
<v-text-field label="Number of Tickets" type="number"
v-model="registration.numtickets" required></v-text-field>
<v-select label="Shirt Size" v-model="registration.shirtsize"
:items="sizes" required></v-select>
<v-btn flat #click.native="step = 2">Previous</v-btn>
<v-btn color="primary" #click.prevent="submit">Save</v-btn>
</v-stepper-content>
</v-stepper-items>
</v-stepper>
</v-app>
</div>
const defaultReg = Object.freeze({
name:null,
email:null,
street:null,
city:null,
state:null,
numtickets:0,
shirtsize:'XL'
});
new Vue({
el: '#app',
vuetify: new Vuetify(),
data () {
return {
step:1,
registration: Object.assign({}, defaultReg),
sizes:['S','M','L','XL']
}
},
methods:{
submit() {
this.registration = Object.assign({}, defaultReg);
this.step = 1;
}
}
})
A simpler approach at resetting your stepper is by using the key prop assigning to it a value and then in the function increasing this value. Something like this:
<template>
<v-stepper
:key="stepperKey"
v-model="e1"
>
...
</v-stepper>
</template>
<script>
export default {
data () {
return {
e1: 1,
stepperKey: 0
}
},
methods: {
increaseKey () { this.stepperKey++ }
}
}
</script>
The key prop or attribute is a build in Vue.js feature. Even if you don't see it it's been used on the back. Changing the key will trigger a re render.
If you have doubt about the key attribute/prop here is a nice article about it

how can i call modal component inside a v-for loop?

I use vuetify https://vuetifyjs.com/en/
My parent component like this :
<template>
<v-container>
...
<v-col
v-for="(item, i) in items"
:key="i"
>
<v-card
>
<v-app-bar dark color="grey">
<v-toolbar-title>Weekly Schedule : {{item.name}}</v-toolbar-title>
<div class="flex-grow-1"></div>
<modal-datepicker :schedule="item" />
</v-app-bar>
</v-col>
...
</v-container>
</template>
<script>
import { mapState } from "vuex";
import modalDatepicker from "../views/modalDatepicker";
export default {
components: {modalDatepicker},
};
</script>
My child component like this :
<template>
<v-dialog
:ref="dialog"
v-model="modal"
:return-value.sync="date"
>
<template v-slot:activator="{ on }">
<v-btn color="success" dark v-on="on">Show datepicker</v-btn>
</template>
<v-row justify="center">
<v-date-picker v-model="date" scrollable>
<div class="flex-grow-1"></div>
<v-btn text color="primary" #click="modal = false">Cancel</v-btn>
<v-btn text color="primary">OK</v-btn>
</v-date-picker>
</v-row>
</v-dialog>
</template>
<script>
import { mapState } from "vuex";
import modalDatepicker from "../views/modalDatepicker";
export default {
components: {modalDatepicker},
props: ['schedule'],
data: () => ({
date: new Date().toISOString().substr(0, 10),
modal: false,
}),
mounted() {
console.log(this.schedule)
},
};
</script>
The code above works, but seems inefficient. because every time the loop, it call modal dialog. I want modal to only be called when user click show datepicker button
how do i do that?
Apologies as I am on my mobile. Have you considered having the modal in your parent component outside of the loop?
You could have a data variable to handle your modal e.g
modal: {
open: false,
schedule: null }
Essentially then when your button is clicked you could add a v-click with a function that controls this data which in turns handles the content in the modal.
Then on your date picker or modal have a function to handle the update/close to clear this data and handle whatever you need too outside.
Then you could combine this into one component.
I see you have vuex you could also use vuex to control your modal and it’s contents.

Vue - closing dialog from child component

I'm using Vue and Vuetify and I'm having trouble closing a dialog from within a child component using $emit. In the main component I'm using v:on:close-dialog="closeDialog" and setting this.dialog = false. I'm trying to call that function from within the child. Trying three different ways:
On the <v-icon>close</v-icon> in the child component, I'm calling a closeDialog method that calls this.$emit('close-dialog').
On the <v-btn>Cancel</v-btn>, I have v-on:click="$emit('close-dialog')".
On the <v-btn>Cancel 2</v-btn>, I have v-on:click="$emit('dialog',false)".
None of those close the dialog or fire off the closeDialog method in the main component. Code is below.
mainComponent:
<template>
<v-flex>
<v-flex xs12 class="text-xs-right">
<v-dialog v-model="dialog" fullscreen hide-overlay
transition="dialog-bottom-transition">
<v-btn fab slot="activator" small color="red" dark>
<v-icon dark >add</v-icon>
</v-btn>
<childComponent v:on:close-dialog="closeDialog" />
</v-dialog>
</v-flex>
</v-flex>
</template>
<script>
import childComponent from './childComponent'
export default {
data(){
return{
dialog: false
}
},
name: 'Test',
components: {
childComponent
},
methods:{
closeDialog: function(){
console.log('close dialog 2');
this.dialog = false;
}
}
}
</script>
childComponent:
<template>
<v-flex xs12>
<v-card>
<v-toolbar dark color="primary">
<v-btn icon dark v-on:click="closeDialog">
<v-icon>close</v-icon>
</v-btn>
<v-toolbar-title>Dialog Test</v-toolbar-title>
<v-spacer></v-spacer>
<v-toolbar-items>
<v-btn dark flat v-on:click="$emit('close-dialog')">Cancel</v-btn>
</v-toolbar-items>
<v-spacer></v-spacer>
<v-toolbar-items>
<v-btn dark flat v-on:click="$emit('dialog',false)">Cancel 2</v-btn>
</v-toolbar-items>
</v-toolbar>
<v-flex xs12 class="px-10">
<v-form ref="form">
<v-text-field
v-model="testField"
:counter="150"
label="Test field"
required
></v-text-field>
</v-form>
</v-flex>
</v-card>
</v-flex>
</template>
<script>
export default {
data: () => ({
testField: ''
}),
methods: {
closeDialog: function(){
console.log('close dialog 1');
this.$emit('close-dialog');
}
}
}
</script>
As you might have guessed, I'm new to Vue and still fumbling my way through it. Any help would be much appreciated.
In your parent you have:
<childComponent v:on:close-dialog="closeDialog" />
it should be (hyphen replaces colon in v-on):
<childComponent v-on:close-dialog="closeDialog" />
or #close-dialog altenatively.
This method, combined with this.$emit('close-dialog'); in your child should work.

Extracting the information in a prop in a Vue child component

I'm passing a object as a prop to a child component but I can't reference one of its elements (user_id) to use in a method in that child component.
My code (slightly abbreviated) is:
<template>
<div class="back">
<v-app id="inspire">
<v-content>
<v-container fluid>
<v-card flat>
<v-card-title>
<div>
<div class="headline">
{{data.title}}
</div>
<span class="grey--text">{{data.user}} said {{data.created_at}}</span>
</div>
<v-spacer></v-spacer>
<v-badge color="deep-orange accent-3" left overlap>
<span slot="badge">7</span>
<v-icon color="grey lighten-1" large>
insert_comment
</v-icon>
</v-badge>
<!--<v-btn color="deep-orange accent-3">5 replies</v-btn>-->
</v-card-title>
<v-card-text v-html="data.body"></v-card-text>
<v-card-actions v-if="own">
<v-btn icon small>
<v-icon color="deep-orange accent-3">edit</v-icon>
</v-btn>
<v-btn icon small>
<v-icon color="red">delete</v-icon>
</v-btn>
</v-card-actions>
</v-card>
<return-button></return-button>
</v-container>
</v-content>
</v-app>
</div>
</template>
<script>
export default {
name: "ShowQuestion",
props: ['data'],
data() {
return {
own: this.Own(),
}
},
methods: {
Own: function () {
return this.UserID() == this.user_id <---HERE BE DRAGONS! (reported as 'undefined')
},
UserID: function () {
... returns the 'sub' from the JWT token
return sub;
}
},
}
</script>
While the correct information is being displayed in the template, I also need to be able to compare the user's ID from the token with that contained in the prop (i.e. data.user_id). My reading suggests the solution will involve converting the object to an array, but that's beyond my current skill level too. I'd appreciate some pointers to a solution.
Thanks,
Tom
If you can render data.user_id in your template you can use it anywhere, but I'd probably do something like this to solve your problem:
props: ['data']
data() {
return {
}
},
computed: {
UserId() {
return //however you get your id
}
},
Then your v-if could just be this:
<v-card-actions v-if="UserId === data.user_id">