How to write a cypress test for vue js router-link? - javascript

Hi I'm new to cypress and trying to make test for vue js <router-link>. Following is the example
About.vue
<router-link data-cy="contactViewLink" :to="{name: 'ContactView'}">Click here</router-link>
About.cy.js
it('Then: User click on Contact View link', () => {
// Do assertions
cy.get('[data-cy="contactViewLink"]').click()
cy.get('#routerPushStub').should('have.been.calledWith', {
name: 'ContactView'
})
})

Related

Vue Toast not showing?

I am trying to add a toast to my code as per below, however no toast message appears and I dont get any errors in the console. The code works however and the invite gets sent. I have used it like this in other files and the toast message appears so im uncertain as to why it wouldn't appear now.
Main.js file where I am importing toast and toast service:
import Toast from 'primevue/toast';
import ToastService from 'primevue/toastservice';
const app = createApp(App);
app.component('Toast', Toast);
app.use(ToastService);
In my file using the toast once an invite is sent if successful I want to display the success message:
<template>
<div class="main-container">
<Button #click="sendInvites"/>
</div>
</div>
</template>
<script>
export default {
data() {
return {
};
},
methods: {
createToast() {
get('CreateInvites', { invites: this.invites.filter((invite) => invite.email.length > 0) }).then((data) => {
if (data.length > 0) {
this.$toast.add({
severity: 'error',
summary: 'Unable to send some invites',
detail: data
})
.join('\n'),
life: 9000,
});
}
else {
this.$toast.add({
severity: 'success',
summary: 'Success!',
life: 3000,
});
}
});
},
},
The ideal location of a Toast is the main application template so that
it can be used by any component within the application.
So, you need to use the <Toast> component in your main file (App.vue) like this-
<template>
<Toast />
<template>
It means the Toast component is mounted while App is loaded into the DOM and it is ready to be displayed upon any event's trigger as you did-
this.$toast.add({
severity: 'error',
summary: 'Unable to send some invites',
detail: data
.map((detail) => {
return `${detail.email}: ${detail.error}`
})
.join('\n'),
life: 9000,
})
For more information, read here- https://primefaces.org/primevue/toast

Nuxt re-rendering for no reason - content popping from one version to another

I'm trying to run an A/B test on my landing page, which is part of a statically-generated Nuxt (v2.15.7) site. For 50% of users, we show a different heading and description on the form.
The issue is that when the page loads, the content sometimes pops from the A test version to B test version (without refreshing the page, or anything else manually causing a re-render).
Here's my code, reduced the most barebones reproduction of the issue:
My landing page component:
<template>
<div>
<SliceMinimalFormHeader
:locale="locale"
v-bind="formContent"
:experiments="experiments"
/>
</div>
</template>
<script>
import SliceMinimalFormHeader from '#/components/SliceMinimalFormHeader.vue'
import { EXPERIMENTS } from '#/data/consts.js'
export default {
components: {
SliceMinimalFormHeader,
},
props: {
locale: {
type: String,
default: 'en-us'
}
},
data() {
const testGroup = Math.random() > 0.5 ? 'A' : 'B'
return {
experiments:
this.locale === 'en-us' && testGroup === 'A'
? [EXPERIMENTS.CALCULATOR]
: [],
formContent: {
'form-heading': '',
'form-description': ''
},
}
},
created() {
const newFormContent = {
'form-heading': 'Heading for the B test version',
'form-description': 'Description for the B test version'
}
if (this.experiments.includes(EXPERIMENTS.CALCULATOR)) {
newFormContent['form-heading'] =
'Heading for the A test version'
newFormContent['form-description'] =
'Description for the A test version'
}
this.formContent = newFormContent
}
}
</script>
Then, inside the child SliceMinimalFormHeader component:
<template>
<section class="grid-12 content-wrapper">
<h4 class="heading-2" :class="$style['form-heading']">
{{ formHeading }}
</h4>
<div :class="$style['form-description']">
{{ formDescription }}
</div>
</section>
</template>
<script>
export default {
props: {
formHeading: {
type: String,
default: ''
},
formDescription: {
type: String,
default: ''
}
}
}
</script>
I'm at my wits' end trying to figure this out!! Any help would be much appreciated.
Vue is a client side framework. All HTML is generated by JS inside the browser. This means that typical Vue app is just very simple HTMl file with almost no HTML and some CSS and Javascript <script> tags...
This is problem for SEO because most crawlers (Google, FB, Twitter) do not execute JS and just scan the HTML returned from server...
To solve this, frameworks as Nuxt was created. They solve the problem by executing Vue app on the server and rendering the HTML on the server - either at request time (Nuxt classic - new HTML is generated each time the request comes) or at build time (Nuxt generate - HTML is generated to a file and same HTML is returned for each request)
In all cases, HTML returned by the server is different but everything else is same. It is still a Vue app, which is executed on the client and once it is started, it overrides any HTML returned from the server...
So in your case you generate some HTML for all users (so either A or B is randomly chosen), this HTML is loaded for all users, but once the Vue app is loaded, it takes the control and renders (randomly) either A or B variant...

Load a VUE component with parameters

I have
<a role="button" v-on:click="setMemberName(item)">
And it calls a method:
methods:{
setMemberName(item) {
alert(item.email);
this.$router.push('about');
}
},
The alert gets fired and the router gets called but I need to send the parameter of item.email and I need to capture that when the 'about' vue gets loaded. I have a simple alert being called using:
,
mounted:function () {
alert("Hello");
},
But I would like it to say "Hello " then the email address like "Hello Smith#jmail.com". I really need the email address so I can call a webservice but Hello is fine for this problem. As you can tell VUE is new to me.
I have tried:
this.$router.push({ name: 'about', params: { itemEmail: item.email } })
but it seems that it never loads the 'about' vue. Thanks for the help.
OK-- edit-- It does get fired if I use the proper case 'About' instead or 'about' but I still need help on the capture side
Code for the about vue: a simple div and some script code:
<script>
export default {
name: 'About',
data() {
return {
}
},
methods:{
},
mounted:function () {
alert("Hello");
},
created(){
},
}
</script>
There are many ways of solving this issue, your are using route params so you need to define the param in the route:
routes: [
{ path: '/about/:email', component: About }
]
Then you can access the param in the About component
In the template:
<div>Email: {{ $route.params.email }}</div>
In the script:
sayHello() {
alert($route.params.email);
}
Note that you could also use route query params or route props, read the docs at: https://router.vuejs.org/guide/
If you update your push to (see docs for full options):
this.$router.push({ name: 'about', query: { itemEmail: item.email } })
You can access the query parameters (See docs here):
this.$router.query.itemEmail

vue.js render ajax data that contains vue.js syntax

Vue.js version is: 2.x
Hi. I'm sending an ajax request in vue js to another page and getting it's source which contains vue.js syntax such as events. When this source is added to property and property added to a template, the ajax data source (that contains vue.js syntax) can not be rendered and does not work properly.
For example template is:
<div id="app">
{{{ foo }}}
</div>
and app.js is:
var app = new Vue({
el: '#app',
data: {
foo: 'bar'
},
mounted(){
this.$http.get('/media').then(function(response){
data = response.body;
Vue.set(app, 'foo', data);
});
},
methods: {
alertVideoLink: function(event){
alert(event.target.href);
}
}
});
In the above app.js code, ajax request returns this code (that is response.body):
Video Link
but this link can't be rendered and does not work properly! I'm testing the render method and some useful hints, but no way found. Please help... Thanks
Sounds like you want to use an Async Component.
Something like...
components: {
'async-media': () => Vue.http.get('/media').then(res => ({
template: res.body,
methods: {
alertVideoLink (event) {
this.$emit('click', event)
}
}
}))
}
Then in your template...
<async-media #click="handleClickEventFromChildComponent" />
Here's an example using a timeout to fake "load" a template
var app = new Vue({
el: '#app',
data: {},
components: {
'async-media': () => new Promise(resolve => {
setTimeout(() => {
resolve({
template: 'Video Link',
methods: {
alertVideoLink(event) {
this.$emit('click', event.target.href)
}
}
})
}, 2000)
})
},
methods: {
handleClickEventFromChildComponent (href) {
console.info('Clicked on', href)
}
}
});
<div id="app">
<p>Wait 2 seconds</p>
<async-media #click="handleClickEventFromChildComponent" />
</div>
<script src="https://unpkg.com/vue#2.4.2/dist/vue.min.js"></script>
#Phil's answer is correct but in my project need to be changed. in this case, the better way is: using global components vs local components because is simple for this work.

How to bind an event on a vue-router template

I've been following a guide to create a vue-router object, but the browser displays the following warning:
[Vue warn]: Property or method "auth_login" is not defined on the
instance but referenced during render. Make sure to declare reactive
data properties in the data option. (found in anonymous component -
use the "name" option for better debugging messages.)
I just added an event binding on a html tag, like the following:
<div id="app">
<router-view>
</router-view>
<script type="text/temptlate" id="t_auth">
<div class="auth">
<router-view></router-view>
</div>
</script>
<script type="text/temptlate" id="t_auth_login">
<div class="auth_login">
<div>
<button class="btn-primary full" id="btn_login" #click="auth_login" #keyup.enter="auth_login">登录</button>
</div>
</div>
</script>
</div>
The JS code is:
(function() {
let getView = (id) => {
tmp = document.getElementById(id)
if (tmp == null) {
return null;
}
return tmp.innerHTML
};
const routes = [{
path: '/auth',
component: { template: getView('t_auth') },
children: [
{ path: 'register', component: { template: getView('t_auth_register') } },
]
}];
const router = new VueRouter({
routes: routes
});
const app = new Vue({
router: router,
el: "#app",
data: {
name: 'Vue.js'
},
// 在 `methods` 对象中定义方法
methods: {
auth_login: function(event) {
// 方法内 `this` 指向 vm
alert('Hello ' + this.name + '!')
}
}
}).$mount('#app')
})();
Why can't it find the auth_login method? What about the lifecycle?
How can I bind the event inside the template ?
The full source code is located there: https://github.com/295421489/reminder-ximu/tree/dev/public
I don't have a direct answer for your question, but this is how you can debug your Vue apps:
Install https://github.com/vuejs/vue-devtools in your Google Chrome browser. You may need to restart the browser for the extension to start working. (I don't remember how I got it the first time)
Once you have Vue dev tools, you will start seeing this in your developer console, whenever you load a Vue app (development build of Vue.js):
Your routes will also show up very well. As you can see, my app above is currently in the route /chapter/1 (that orange box on the left side)
Click on "Send to Console" and the $vm instance will become available in your developer console.
Here, you can find if your auth_login method is available or not, for your route. And you can also do a lot more debugging for your app.
If you want a working Vue app (with routes) to test, you will find a jsFiddle in this answer: https://stackoverflow.com/a/40215123/654825
Hope it helps!
I solved this question.
The error is can't find the method,I think it is because of scope. So, I created a component firstly:
var t_auth_login = Vue.extend({
template: getView('t_auth_login'),
// 在 `methods` 对象中定义方法
methods: {
auth_login: function(event) {
}
});
and the routes values as :
const routes = [{
path: '/auth',
component: t_auth}]
everything is ok.

Categories