global mixin not called in vue3 using render function - vue.js

I want to use a global mixin in vue3.
Using the mixin property - mixin:[mixinName] in createApp does not work.
Using the method .mixin(mixinName) will work.
What is the difference?
Not working:
return createApp({
mixins: [utilsMixin],
setup() {
return () => h(HelloWorld, {}, () => childComponents)
}
})
Working Version:
return createApp({
setup() {
return () => h(HelloWorld, {}, () => childComponents)
}
}).mixin(utilsMixin)

Related

Mocking mixin in Vue Test libs

I'm having a couple of issues with my Vue test libs. I am trying to test a mixin. It requires setting the route and mocking a function. Here is my code
MIXIN
export const CampaignNotifier = {
mounted () {
// Create tagname with dynamic currency parameter from banking app
let routeName = this.$route.name
let queryParamCurrency = (this.$route.query.currency) ? `- ${ this.$route.query.currency.toUpperCase() }` : '-'
this.campaignTagName = (BRAZE_TAG_MAPPING[routeName]) ? BRAZE_TAG_MAPPING[routeName].replace(/-/, queryParamCurrency) : null
if (this.campaignTagName) {
this.$appboy.logCustomEvent(this.campaignTagName)
}
},
}
TEST:
import { expect } from 'chai'
import { shallowMount, createLocalVue } from '#vue/test-utils'
import VueRouter from 'vue-router'
import sinon from 'sinon'
import { CampaignNotifier } from '#/mixins/campaignNotifier'
let wrapper
function factory (routeName, currency) {
let localVue = createLocalVue()
localVue.use(VueRouter)
let routes = [
{
path: routeName,
name: routeName,
query: {
currency
}
}
]
let router = new VueRouter({
routes
})
let Component = {
render () { },
mixins: [CampaignNotifier],
localVue,
router,
mocks: {
$route: {
path: routeName,
query: {
currency
}
},
$appboy: {
logCustomEvent: () => {}
}
}
}
return shallowMount(Component)
}
describe('#/mixins/campaignNotifier', () => {
it('Campaign Notifier is not called if route not configured correctly', () => {
wrapper = factory('before-begin', 'EUR')
console.log('$route ***', wrapper.vm.$route)
sinon.spy(wrapper.vm.$appboy, 'logCustomEvent')
expect(wrapper.vm.$appboy.logCustomEvent).not.toHaveBeenCalled()
})
})
Issues I am encountering:
When I mock the $route and console.log it, it returns undefined. I tried mocking it and also using VueRouter. Neither worked.
I am trying to mock the global prototype / method $appboy.logCustomEvent I get:
[Vue warn]: Error in mounted hook: "TypeError: Cannot read property 'logCustomEvent' of undefined"
Any help would be greatly appreciated. Thanks
You're incorrectly combining mounting options with the component definition itself, so your mocks aren't actually getting installed, leading to the errors you observed.
Mounting options should be passed as the second argument to shallowMount:
function factory() {
let Component = {
render() { },
mixins: [CampaignNotifier],
}
let mountingOptions = {
localVue,
router,
mocks: {
$route: {
path: routeName,
query: {
currency
}
},
$appboy: {
logCustomEvent: () => { }
}
}
}
return shallowMount(Component, mountingOptions)
}

Nuxt: Create a plugin that automatically adds a computed to component

I would like to create a Nuxt plugin that automatically adds a computed to components that have a certain property (without using a mixin).
For example, any component that have a addComputedHere property:
export default {
data() {
return {}
},
computed: {
myComputed: () => 'foo'
},
addComputedHere: true
}
would turn into:
export default {
data() {
return {}
},
computed: {
myComputed: () => 'foo',
injectedComputed: () => 'bar' // Injected
},
addComputedHere: true
}
So far, I'm not sure what's the best solution among using a Nuxt plugin/module/middleware or simply a Vue Plugin (if it's feasible).
How would you do it?
If anybody is in the same case, I found a solution by creating a Vue plugin that applies a mixin to customize the component in beforeCreate:
import Vue from 'vue';
const plugin = {
install(Vue, options) {
Vue.mixin({
beforeCreate() {
if (this.$options.addComputedHere) {
this.$options.computed['injectedComputed'] = () => 'bar';
}
}
})
}
};
Vue.use(plugin);

Mock of store action of vuex does not mocked

I have small vue component that on created hook dispatch some action
#Component
export default class SomeComponent extends Vue {
created() {
store.dispatch('module/myAction', { root: true });
}
}
and I wrote next test
const localVue = createLocalVue();
localVue.use(Vuex);
localVue.use(VueRouter);
const localRouter = new VueRouter();
describe('SomeComponent.vue Test', () => {
let store: any;
beforeEach(() => {
store = new Vuex.Store({
modules: {
module: {
namespaced: true,
actions: {
myAction: jest.fn()
}
}
}
});
});
it('is component created', () => {
const wrapper = shallowMount(SomeComponent, {
localVue,
store,
propsData: {}
});
expect(wrapper.isVueInstance()).toBeTruthy();
});
});
but for some reason the "real" code are executed and I got a warning
isVueInstance() is deprecated. In your test you should mock $store object and it's dispatch function. I fixed typo in created(), here's my version of SomeComponent and working test, hope that would help.
#Component
export default class SomeComponent extends Vue {
created () {
this.$store.dispatch('module/myAction', { root: true })
}
}
import { shallowMount, Wrapper } from '#vue/test-utils'
import SomeComponent from '#/components/SomeComponent/SomeComponent.vue'
let wrapper: Wrapper<SomeComponent & { [key: string]: any }>
describe('SomeComponent.vue Test', () => {
beforeEach(() => {
wrapper = shallowMount(SomeComponent, {
mocks: {
$store: {
dispatch: jest.fn()
}
}
})
})
it('is component created', () => {
expect(wrapper.vm.$store.dispatch).toBeCalled()
expect(wrapper.vm.$store.dispatch).toBeCalledWith('module/myAction', { root: true })
})
})
Also keep in mind that when you test SomeComponent or any other component you should not test store functionality, you should just test, that certain actions/mutations were called with certain arguments. The store should be tested separately. Therefore you don't need to create real Vuex Store when you test components, you just need to mock $store object.

Vue mixins in Laravel Inertia

I'm trying to create a global helper function via Vue mixin on a Laravel Inertia project to no avail:
//app.js
Vue.mixin({
methods: {
myFunction() {
return 'Returnign from myFunction';
},
},
});
new Vue({
...
}).$mount(app);
.
//MyComponent.vue
console.log(myFunction()); // ReferenceError: myFunction is not defined
On a standalone Vue.JS project, this works. Maybe there's something behind the scene in Inertia that prevents the mixin from loading. Can somebody help me understand why this is happening?
Thank you.
you need () on your function
Vue.mixin({
methods: {
myFunction() {
return 'Returnign from myFunction';
},
},
});
and then you missed this before your mixin function
console.log(this.myFunction());
You need to add the function in the mixin of the createInertiaApp method in your app.js file.
For instance:
createInertiaApp({
resolve: (name) => require(`./Pages/${name}.vue`),
setup ({ el, app, props, plugin }) {
return createApp({ render: () => h(app, props) })
.use(plugin)
.mixin({
methods: {
myFunction: () => {
return 'Returning from myFunction';
}
}
})
.mount(el)
}
})

Is there a way to add instances to already mounted Vue component in vue-test-utils?

I have few unit tests for a Vue component. In most of the tests, the shallowMounted component just need computed properties. But for two test I need a store(Vuex) instance. Is there a way to add instances to the already shallowMounted component? May be code below will help to understand. Its just an example. Thank you.
describe(('Vue component test') => {
beforeEach(() => {
wrapper = shallowMount(Component, {
computed: {
errors: () => null,
isLoading: () => false,
}
})
});
describe('Test 1', => {
it('is a vue instance', () => {
expect(wrapper...);
});
});
describe('Test 2' => {
it('is a vue component', () => {
expect(wrapper...);
});
})
describe('Test with store instance', {
// Add store(Vuex) instance to the `wrapper` defined inside beforeEach() above
// Then use the wrapper
expect(wrapper...);
});
});
what do you mean by store instance? A instance of Vuex.Store ?
If so you could try to do:
import Vuex from 'vuex';
beforeEach(() => {
const store = new Vuex.Store({
// mock your store data here
});
wrapper = shallowMount(Component, {
store,
// your other properties, e.g. computed
});
});