Vue-test-utils cannot call text on empty wrapper - vue.js

I have the following html template
<div>
<li class="posted-comment" v-for="commentDetail in commentDetail" :key="commentDetail.id"
#mouseover="hoverIn()" #mouseout="hoverOut()">
<div class="comment-user-avatar"><img :src="commentDetail.img_url"></div>
<div class="comment-user-details">
<span class="distinct-user-name">{{ commentDetail.name}}</span>
<span class="distinct-user-title">{{ commentDetail.user }}</span>
<span class="full-stop">.</span>
<span class="distinct-time-stamp">{{ commentDetail.time}}</span>
</div>
<div class="comment-info-block-wrapper">
<div id="distinct-user-comment">
{{ commentDetail.text}}
</div>
</div>
</li>
Calling wrapper.find('li.posted-comment .comment-info-block-wrapper #distinct-user-comment') says cannot call text on empty wrapper, looks like the "distinct-user-comment" id cant be found
describe('Comment.vue', () => {
it('renders distinct user comment class', () => {
const string = 'So what the German automaker is likely to focus on today is the bigger picture. This will be the first time we see the Taycan free from any prototype bodywork.';
const wrapper = shallowMount(Comment)
expect(wrapper.find("li.posted-comment .comment-info-block-wrapper #distinct-user-comment").text()).equal(string)
})
})

Related

How can I make my section hide only after the submit button is pressed. right now the section disappears after I press one letter

vue.js, how can I make my section hide only after the submit button is pressed. right now the section disappears after I press one letter. I want the V-if and V-else to activate only after the user has submitted their request. or if routing the results on to a different page would easier id like to go that route also.
<template>
<div class="home">
<section id="whiteClawVideo" class="videoWrapper d-block w-100">
<div class="video-container fluid">
<iframe width="100%" height="600" src="https://www.youtube.com/embed/JORN2hkXLyM?
autoplay=1&loop=1" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope;
picture-in-picture" allowfullscreen></iframe>
</div>
</section>
<form #submit.prevent="SearchMovies()" class="search-box">
<input type="text" placeholder="What are you looking for? " v-model="search" />
<input type="submit" value="Search">
</form>
<div class="movies-list" v-if="search !== ''" >
<div class="container">
<div class="row">
<div class="col-3" v-for="movie in movies" :key="movie.imdbID">
<router-link :to="'/movie/'+movie.imdbID" class="movie-link">
<img class="movieImg" height="100%" :src="movie.Poster" alt="Movie Poster" />
<div class="type">{{ movie.Type }}</div>
<div class="detail">
<p class="year">{{movie.Year}}</p>
<h3>{{ movie.Title }}</h3>
<p>{{movie.imdbID}}</p>
</div>
</router-link>
</div>
</div>
</div>
</div>
<div class="container" v-else>
<MovieSection />
<SecondMovieSection />
</div>
</div>
</template>
import { ref } from 'vue';
import env from '#/env.js';
import MovieSection from '#/components/MovieSection.vue';
import SecondMovieSection from '#/components/SecondMovieSection.vue'
export default {
components: {
MovieSection,
SecondMovieSection
},
setup () {
const search = ref("");
const movies = ref([]);
const SearchMovies = () => {
if (search.value !== "") {
fetch(`API_HERE`)
.then(response => response.json())
.then(data => {
console.log(data)
movies.value = data.Search;
})
}
}
return {
search,
movies,
SearchMovies
}
}
}
Well, it closes once you type a single character because search is a model - it updates on every keypress you do within input it's bound to. What you wanna do instead is hide form based on whether you have entries in your movies array or not, so try changing v-if="search !== ''" to v-if="!movies.length"

How to unshift to append under spicific div in Vue

I have comments and this comment containe likes, My propblem is when I try to push replay to the comment it is append to the wrong comment, So I used this.$el; to get the target comment bu the propblem is unshift/push just use with list and it shows error push is not a function
My qustion is how I can use unshift/push to append under spicific div not list
async addReplay(comment, e){
const element = this.$el; //this to get the target div
}
here the post replay function
async getaddReplay(id, setReplayPlace){
await axios.get('/account/api/auth/user/')
.then(response => {
this.currentUserImage = response.data.profile_image
})
const replayData = {
content: this.commentReplayContent,
video: this.$route.params.video_id,
parent: id
}
await axios.post(`/video/api/video/comment/${id}/replay/create/`, replayData)
.then(response => {
console.log(this.setReplayPlace)
// here I want append the item to spicific div
this.setReplayPlace.unshift({ content: this.commentReplayContent, author: this.$store.state.user.username, id:response.data.id, author_image:this.currentUserImage, video:this.$route.params.video_id ,likes:0, total_parents:0, check_like: false, publish:'now'})
this.commentReplayContent = ''
})
.catch(error => {
console.log(error)
})
}
Edit
Here there are comments with replies component and I pass the data to repliy component using props
<ul v-for="(comment, index) in comments" :key="index">
<li class="comment-object">
<div class="image-container">
<img class="profile-pic" :src="comment.author_image" v-on:change="currentUserImage" alt="profile picture" id="user_video_comment_profile_image" refs="user_video_comment_profile_image" />
</div>
<div class="comment-text">
<h2 class="username" style="color: #C2C3C4">{{comment.author}} <span class="muted">· {{comment.publish}}</span>
<DeleteComment :comment="comment" v-if="comments || index" :comments="comments" :index="index" />
</h2>
<p class="comment">{{comment.content}} </p>
<RepliesJustAdded #justAddedReplies="addRepliesToParent" :replaiesJustAdded="replaiesJustAdded" :setReplayPlace="setReplayPlace" :allReplies="allReplies" :replaiesData="replaiesData" v-if="replaiesJustAdded || setReplayPlace || allReplies || replaiesData" />
</div>
</li>
</ul>
The Replay component
<ul v-for="replay in replies" :key="replay.id" id="video_comments_replies" >
<li class="comment-object">
<div class="image-container">
<img class="profile-pic" :src="replay.author_image" alt="profile picture" id="user_video_comment_profile_image" refs="user_video_comment_profile_image" />
</div>
<div class="comment-text">
<h2 class="username" style="color: #C2C3C4">{{replay.author}} <span class="muted">· {{replay.publish}}</span></h2>
<p class="comment">{{replay.content}} </p>
<RepliesActionButtons :replay="replay" />
</div>
</li>
</ul>
I didn't find the line where you are trying to call push but I thing I know what's wrong: your are trying to call push but array-like collections don't have such method. You should convert your collection to array before calling push. Try something like that:
// create a `NodeList` object
const divs = document.querySelectorAll('div');
// convert `NodeList` to an array
const divsArr = Array.from(divs);
After that all methods of Array.prototype will be available.

How to unshift/push image to list in vue

I have list of comments and I could append username of user, content and date to the list put when I tried to append the user image it shows null and I tried to append image from internet and also it shows null
<ul v-for="comment in comments" :key="comment">
<li class="comment-object">
<div class="image-container">
<img class="profile-pic" :src="'http://192.168.63.200:8000' + comment.author_image" v-on:change="currentUserImage" alt="profile picture"/>
</div>
<div class="comment-text">
<h2 class="username" style="color: #C2C3C4">{{comment.author}} <span class="muted">· {{comment.publish}}</span></h2>
<p class="comment">{{comment.content}} </p>
</div>
</li>
</ul>
await axios.post('/video/api/video/comments/create/', commentData)
.then(response => {
this.comments.unshift({ content: this.commentContent, author: this.$store.state.user.username, author_image: 'https://image.shutterstock.com/image-photo/surreal-image-african-elephant-wearing-260nw-1365289022.jpg', likes:0, publish:'now'})
})
The problem was in <img class="profile-pic" :src="'http://192.168.63.200:8000' + comment.author_image" v-on:change="currentUserImage" alt="profile picture"/>
I just removed the url http://192.168.63.200:8000 and it worked

Vue.js how to use radio buttons inside v-for loop

I'm trying to use radio buttons, so that users can choose one of the photos as their profile photo:
<ul v-for="p in myPhotos">
<li>
<div>
<div>
<div>
photo id: {{p.imgId}}
</div>
<input type="radio" v-model="profileImg" value="p.imgId"> Choose as profile image
</div>
<div><img v-bind:src="BASE_URL +'/uploads/' + userId + '/'+ p.imgId" /> </div>
</div>
</li>
</ul>
The values are otained like this:
created () {
axios.get(this.BASE_URL + '/profile/g/myphotos/', this.jwt)
.then( res => {
this.myPhotos = res.data.photos;
this.showUploadedPhoto = true;
this.profileImg = res.data.profileImg
})
.catch( error => {
console.log(error);
})
},
When an photo is chosen, the profileImg variable should be set to that photo's imgId.
The problem is how to let users to choose only one photo as profile image inside this v-for loop?
Update: a photo my myPhotos object that I'm iterate over:
By providing name you can treat it as one element
var app = new Vue({
el:"#app",
data:{
images:[{imgId:1},{imgId:2},{imgId:3}],
profileImg:2
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div>
<div v-for="image in images">
<input type="radio" v-model="profileImg" name="profileImg" :value="image.imgId"> Choose as profile image
</div>
You have selected : {{profileImg}}
</div>
</div>
How about setting name attribute for the input field
<ul v-for="p in myPhotos">
<li>
<div>
<div>
<div>
photo id: {{p.imgId}}
</div>
<input type="radio" name="user_profile_photo" v-model="profileImg" :value="p.imgId"> Choose as profile image
</div>
<div><img v-bind:src="BASE_URL +'/uploads/' + userId + '/'+ p.imgId" /> </div>
</div>
</li>
</ul>

Data Reactivity On Post Request Vue

Im having issues with my data not reacting correctly when a new object is added to the array of data. I am currently looping through an array of engagements that belong to a client like this
<div class="row mx-2 px-2 justify-content-between" v-if="!engagementLoaded">
<div class="card mb-3 shadow-sm col-lg-5 col-md-3 p-0" v-for="(engagement, index) in engagement" :key="index">
<div class="d-flex justify-content-between card-header">
<h3 class="m-0 text-muted">{{ index + 1 }}</h3>
<h5 class="align-self-center m-0"><span>Return Type: </span> {{ engagement.return_type }} </h5>
</div>
<div class="card-body text-left p-0 my-1">
<h5 class="p-4"><span>Year: </span> {{ engagement.year }} </h5>
<hr class="my-1">
<h5 class="p-4"><span>Assigned To: </span> {{ engagement.assigned_to }} </h5>
<hr class="my-1">
<h5 class="p-4"><span>Status: </span> {{ engagement.status}} </h5>
</div>
<div class="card-footer d-flex justify-content-between">
<router-link to="#" class="btn">View</router-link>
<router-link to="#" class="btn">Edit</router-link>
</div>
</div>
</div>
Everything works fine until I add a new engagement to the array. My AddEngagement Component is seperate but this is the form and script for it.
<form #submit.prevent="addEngagement" class="d-flex-column justify-content-center">
<div class="form-group">
<select class="form-control mb-3" id="type" v-model="engagement.return_type">
<option v-for="type in types" :key="type.id" :value="type">{{ type }}</option>
</select>
<input type="text" class="form-control mb-3" placeholder="Year" v-model="engagement.year">
<input type="text" class="form-control mb-3" placeholder="Assign To" v-model="engagement.assigned_to">
<input type="text" class="form-control mb-3" placeholder="Status" v-model="engagement.status">
<div class="d-flex justify-content-between">
<button type="submit" class="btn btn-primary d-flex justify-content-start">Create</button>
<router-link v-bind:to="'/client/' +client.id+ '/engagements'" class="btn btn-secondary float-right">Dismiss</router-link>
</div>
</div>
</form>
This is the addEngagement Method for the form
methods: {
addEngagement(e) {
if(!this.engagement.return_type || !this.engagement.year ){
return
} else {
this.$store.dispatch('addEngagement', {
id: this.idForEngagement,
client_id: this.client.id,
return_type: this.engagement.return_type,
year: this.engagement.year,
assigned_to: this.engagement.assigned_to,
status: this.engagement.status,
})
e.preventDefault();
}
e.preventDefault();
this.engagement = ""
this.idForEngagement++
this.$router.go(-1);
},
},
I think the issue is happening here but im not sure
this.$router.go(-1);
Ive tried this as well
this.$router.push({path: 'whatever route'})
and it did not change either
Somehow I need the parent component EngagementsList to react correctly to my newly added engagement that is submitted from the AddEngagement componenet if that makes sense..
here is the code for the addEngagement in the Vuex
addEngagement(context, engagement) {
axios.post(('/engagements'), {
client_id: engagement.client_id,
return_type: engagement.return_type,
year: engagement.year,
assigned_to: engagement.assigned_to,
status: engagement.status,
done: false
})
.then(response => {
context.commit('getClientEngagement', response.data)
})
.catch(error => {
console.log(error)
})
},
Although I can't see your code, I'm pretty certain I know the problem. You have an array but it's not reactive to the objects within. This is because Vue doesn't know to observe this array.
If you want your array to be reactive, use Vue.set from within your store model.
Vue.set(this/state, 'array_name', Array<Of objects>)
This will make sure vue watches for changes within the Array.
For your use case, you will need to append the engagement object to the existing array and then use that array:
const engagements = state.engagements
engagements.push(engagement)
Vue.set(state, 'engagements', engagements)
So i've tried different configurations of this. I changed my state description that the v-for is iterating to clientengagements
engagement now equals clientengagements
here is the mutation currently
getClientEngagements(state, engagement, clientengagements) {
state.clientengagements = clientengagements
clientengagements.push(engagement)
Vue.set(state, 'clientengagements', clientengagements)
},
Which is not working. Ive also tried
getClientEngagements(state, clientengagements) {
state.clientengagements = clientengagements
clientengagements.push(engagement)
Vue.set(state, 'clientengagements', clientengagements)
},
Which will tell me that "engagement" is not defined
If anyone is still able to help this is the Action
addEngagement(context, engagement) {
axios.post(('/engagements'), {
client_id: engagement.client_id,
return_type: engagement.return_type,
year: engagement.year,
assigned_to: engagement.assigned_to,
status: engagement.status,
done: false
})
.then(response => {
context.commit('getClientEngagements', response.data)
})
.catch(error => {
console.log(error)
})
},
The response.data is mutating my clientengagements array to just a single object of the newly added engagement..
context.commit('getClientEngagements`, response.data)
Is commiting to this mutation
getClientEngagements(state, engagement, clientengagements) {
state.clientengagements = clientengagements
clientengagements.push(engagement)
Vue.set(state, 'clientengagements', clientengagements)
},