Vue Test Utils with Jest - Mixins not working - vue.js

I have created a local Vue app and I need to mix a method into all components. The problem is whenever I mount the app the method does not appear to be mixed for child components. Here is my code:
import { createLocalVue, mount } from '#vue/test-utils'
import {gc} from '.....';
import .... from ....
const App = createLocalVue()
App.use( .... )
App.mixin({
methods: {
gc: key => gc(key)
}
})
export const Wrapper = mount(AppComponent, {
App,
i18n,
router,
store,
...
})
Whenever I import "Wrapper" into any test it fails to the first mounted component with message:
TypeError: _vm.gc is not a function
How can I include mixins to propagate to all the child components?

mount has no App option. Instead, there's localVue to supply Vue copy.
It should be:
mount(AppComponent, {
localVue: App,
...

Related

Vue build didnt include my function to output .js file

Works fine in dev mode, but after build process vue not includes setupToken function (from #/api.js) to app.js output file.
// App.vue
//...
import { setupToken } from '#/api
export default {
mounted () {
this.setupToken() // TypeError: this.setupToken is not a function
}
}
// #/api.js
import { mande } from 'mande'
export const postsAPI = mande(`${process.env.VUE_APP_ROOT_URL}/api/v1/posts`)
export const setupToken = ({ token }) => {
postsAPI.options.headers.Authorization = 'Bearer ' + token
}
I guess the problem with webpack config (im using default one), but not sure how to fix it.
setupToken does not exist on the object you're exporting from App.vue.
You are importing setupToken from #/api so you have to call it directly, i.e. setupToken() and not as an instance method, i.e. this.setupToken()
WebPack sees you are not using the method you're importing from #/api (because you are calling it as an instance method) so it tree-shakes it out.
BTW, try using TypeScript, it would have let you know of that error.
Import api.js in main.js
import Vue from 'vue'
import App from './App.vue'
import api from './api'
Vue.config.productionTip = false
new Vue({
api,
render: function (h) {
return h(App)
},
}).$mount('#app')
Modify api.js:
import Vue from 'vue'
Vue.mixin({
methods: {
setupToken({ token }) {
postsAPI.options.headers.Authorization = 'Bearer ' + token
}
}
}

Theory: Axios Calls (Specifically for VueJS)

On component mount(), Axios fetches information from the back end. On a production site, where the user is going back and forth between routes it would be inefficient to make the same call again and again when the data is already in state.
How do the pros design their VueJS apps so that unnecessary Axios calls are not made?
Thank you,
If the data is central to your application and being stored in Vuex (assuming that's what you mean by "state"), why not just load it where you initialise your store?
// store.js
import Vue from 'vue'
import Vuex from 'vuex'
import axios from 'wherever'
Vue.use(Vuex)
const store = new Vuex.Store({
state: {
centralData: {}
},
mutations: {
setCentralData (state, centralData) {
state.centralData = centralData
}
},
actions: {
async loadCentralData ({ commit }) {
const { data } = await axios.get('/backend')
commit('setCentralData', data)
}
}
}
// initialise
export const init = store.dispatch('loadCentralData')
export default store
If you need to wait for the dispatch to complete before (for example) mounting your root Vue instance, you can use the init promise
import Vue from 'vue'
import router from 'path/to/router'
import store, { init } from 'path/to/store'
init.then(() => {
new Vue({
store,
router,
// etc
}).$mount('#app')
})
You can import and use the init promise anywhere in order to wait for the data to load.

importing store to a vuejs nuxt project

I'm trying to write a simple plugin for my Vue.js(Nuxt) project. I came across this post Adding Mutations to Vuex store as part of Vue Plugin but still unable to get it working.
Here is my application structure.
~ is root
~/plugins/HTTP/index.js
~/plugins/HTTP/_store/ => index.js, actions.js, getters.js, mutations.js
~/plugins/HTTP/_api/ => index.js
**Global Store**
~/store/index.js
~/store/modules/
~/store/modules/testing => index.js, actions.js, getters.js, mutations.js
in my ~/plugins/HTTP/index.js, I have the following code
import Vue from 'vue';
import store from '~/store';
const HTTP = {
install(vue, { store }){ // Now you plugin depend on store
if(!store){
throw new Error('Please provide vuex plugin.')
}
// register your own vuex module
store.registerModule({store})
}
}
export default HTTP;
Vue.use(HTTP)
In my ~/store/index.js I have the following code:
import Vuex from 'vuex'
import testingModule from './modules/testing'
const state = () => {
return new Vuex.Store({
modules:{
testing: testingModule
}
})
}
export default state
When I try to run it, it gives me the following message:
Cannot destructure property `store` of 'undefined' or 'null'.
What did I do wrong here?
You aren't passing any properties so the error is correct. You need pass in an options object when you tell it to use. It can be empty, but it needs an object.
import Vue from 'vue';
import store from '~/store';
const HTTP = {
install(vue, { store }){ // Now you plugin depend on store
if(!store){
throw new Error('Please provide vuex plugin.')
}
// register your own vuex module
store.registerModule({store})
}
}
export default HTTP;
Vue.use(HTTP, {}) // <---------- Empty object to avoid allow destructuring.

Vuejs - vuex lazy load

I have a fairly large VueJS SPA, and I just wanted to load the vuex modules on certain routes, using the lazy load.
I was following this article to reproduce this - https://alexjoverm.github.io/2017/07/16/Lazy-load-in-Vue-using-Webpack-s-code-splitting/
However this is giving me an error in vuex.
Folder structure
root
|- store
| |- modules
| | |- auth.js
| | |- module2.js
| |- store.js
|- app.js
auth.js
const state = {
var1: {},
var2: false
}
const mutations = {
'MUTATION_1'(state, obj) {
// logic
}
}
const actions = {
action1({commit}) {
// logic
}
}
const getters = {
var1: state => state.var1,
var2: state => state.var2
}
export default {
state,
mutations,
actions,
getters
}
store.js -
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
const store = new Vuex.Store();
import('./modules/auth.js').then(auth => {
store.registerModule('/login', auth);
});
export default store;
app.js -
import Vue from 'vue';
import store from './store/store';
import VueRouter from 'vue-router';
import { routes } from './routes/routes';
// vue-router config
Vue.use(VueRouter);
const router = new VueRouter({
mode: 'history',
routes
});
const app = new Vue({
el: '#app',
store,
router
});
error -
[vuex] unknown getter: var1
Any suggestion?
store.registerModule('/login', auth);
The above code registers the auth module with the namespace of login so in order to access its state, getters, mutations, and actions from the store you have to prefix all of those with the path login/. You got the error because you probably tried store.getters.var1 when you should have called store.getters['login/var1'].
Getter is called before the login module was loaded. This happens because import() is asynchronous function, and the module was registered sometime after computed watcher was created.
return store.state.login && store.getters['login/var1'] inside component computed getter will subscribe your component to store.state changes first, then after login module loaded and registered store.state will change and trigger component computed getter and automatically subscribe to any state.login changes

Vue.js / Mixins - Is there a way to get the global mixin-object outside of the vue component?

I am new with Vue.js
I am using Vue.js 2.4.4.
I have created the global mixin in my app.js file:
...
import router from './router'
...(some imports and plugins definitions)
Vue.use(VeeValidate);
Vue.component(VuePassword);
...
Vue.mixin({
data: function(){
return {
get auth(){
return Auth;
}
}
}
});
const app = new Vue({
el: '#root',
template: `<app></app>`,
components: { App },
router
});
This mixin imports some Auth object with validation methods e.t.c which needed to be in every component.
All of my components can check this mixin and it's working fine.
But I need to check the auth state after every route request, and I want to use my currently existing mixin, so I am trying to make something like this in my router.js file:
import Vue from 'vue'
import VueRouter from 'vue-router'
...
Vue.use(VueRouter);
const router = new VueRouter({
routes:[
...
]
});
router.beforeEach((to, from, next) => {
if(to.meta.requiresAuth) {
if(...call to mixin method) {
next();
} else {
next('/');
}
} else {
next();
}
});
export default router
Question:
Is there a way to get the global mixin object and change it's inner values or can you please give some small advise or example what is the right solution to this kind of tasks?
Or should I use the plugins instead of mixins?
I would rather create a seperate file for auth and not make it a mixin. Then using Vue.use() which will set auth on the vue object.
A sample of what the files might look like:
auth.js
export default function(Vue) {
Vue.auth = {
// do your auth logic
}
}
Then in your main js file
main.js
import Auth from './auth.js'
Vue.use(Auth);
Then you should be able to use Vue.auth
Another option would be keep using the mixin and pass the value to a store (like vuex) or create your own if your project is small...