Vue + Jest global config carrying over into spec files - javascript

I'm using VueJS and Jest for unit testing my components.
I'm also using the Bootstrap Vue library for styling. I need to use this plugin in my Jest tests in order to remove some console warnings about unknown plugins.
I have created a setup file as so:
import { createLocalVue } from '#vue/test-utils'
import BootstrapVue from 'bootstrap-vue'
const localVue = createLocalVue()
localVue.use(BootstrapVue)
And configured Jest to use this before every test.
setupFiles: ['<rootDir>/tests/unit/setup']
However, to remove the warnings from the console, I need to use the localVue instance when mounting the component:
const wrapper = shallowMount(MyComponent, {
localVue,
propsData: { value: 'someVal }
})
However there is no way that I can see to get the localVue instance created in the setup.js into the test spec files.
If I do this:
import Vue from 'vue'
import BootstrapVue from 'bootstrap-vue'
Vue.use(BootstrapVue)
It works fine, but this is bad as we should not be using the Global Vue instance in Jest tests.
Is there a way to do what I want to do, or am I going to have to construct the Bootstrap Vue plugins (and others as they come along...) into every single test file?

You could try to assign the localVue variable as a global variable in your setupFiles. This would allow you access localVue variable in every test, like so:
import { createLocalVue } from '#vue/test-utils'
import BootstrapVue from 'bootstrap-vue'
global.localVue = createLocalVue()
global.localVue.use(BootstrapVue)
Then use it like this in your test:
const localVue = global.localVue
const wrapper = shallowMount(MyComponent, {
localVue,
propsData: { value: 'someVal' }
})

Related

Vitest with element plus unplugin unknown extension for scss

I'm trying to run tests using Vitest on a Vue.js app that uses Element Plus registered as a plugin.
If I use mount on a component that contains an Element Plus component, I get the following error:
TypeError: Unknown file extension ".scss" for /home/projects/vitejs-vite-zcdxhn/node_modules/element-plus/theme-chalk/src/button.scss
The issue can be replicated on this StackBlitz.
My vite.config.js file looks like this:
import { defineConfig } from 'vite';
import vue from '#vitejs/plugin-vue';
import ElementPlus from 'unplugin-element-plus/vite';
export default defineConfig({
plugins: [vue(), ElementPlus({ useSource: true })],
});
My HelloWorld.vue component looks like this:
<script setup>
import { ElButton } from 'element-plus';
</script>
<template>
<el-button type="primary">Hello</el-button>
</template>
My HelloWorld.spec.js looks like this:
import { test, expect } from 'vitest';
import HelloWorld from '../HelloWorld.vue';
import { mount } from '#vue/test-utils';
test('hello world test', async () => {
const wrapper = mount(HelloWorld);
expect(wrapper.text()).toContain('Hello');
});
The seems to be specifically related to the ElementPlus({ useSource: true })] "unplugin" in plugins in vite.config.js because when I remove that, the problem goes away.
I've reviewed the docs for the various tools (Element Plus, Vite, Vitest), but I've not been able to find how to get this working.
Is there a custom test config that needs to be applied?

How do I fix "the requested module does not provide an export named 'default'"?

I'm developing app using JS and Vue.js and get error on line:
import Vue from 'vue'
I'm getting this:
Uncaught SyntaxError: The requested module
'/node_modules/.vite/vue.js?v=6dba2ea6' does not provide an export
named 'default'
I googled that might be caused by old Vue version, in my package.json vue version is 3.2.6, but
npm view vue version
returns 2.6.14, I tried to upgrade it with Vue CLI
vue upgrade
but npm command still return 2.6.14
I hope you could help me, what did I wrong or it is not even versions problem? Thanks!
The reason it didn't work is that Vue provides a named export, whereas you are trying to import it as though it had a default export.
To make a named import (which you must do with named exports), you need to wrap the name of the export you want to import in curly braces, so {} around Vue like this:
import { Vue } from 'vue';
// ^^^ name of export
It will work
The thing you want to do is import vue but it doesnot have a default export function or either the default thing to export is not set in vue module. So you have to select function named vue by adding curly braces.
If it had a default export function, then your code would have worked and in that case you could write anything in place of vue like below:
import anyname from 'vue'
anyname is name whatever you want.
This worked for me:-
import * as Vue from 'vue';
and similarly for different packages:-
import * as Vuex from 'vuex';
import * as VueRouter from 'vue-router';
As of time of writing:-
"devDependencies": {
...
"vue": "^3.2.45",
Another solution is to use the createApp() function like this:
import { createApp } from 'vue';
createApp(App).mount('#app')
I'm not experienced in Vue JS, but it looks like they no longer export a single object. Ranger a collection of things.
Usually as of Vue 2, in the src/main.js file, we’re bootstrapping the app by calling a new Vue as a constructor for creating an application instance.
import Vue from "vue";
import App from "./App.vue";
import router from './router'
const app = new Vue({
router,
render: h => h(App)
});
For Vue 3 the initialization code syntax has changed and is much cleaner and compact
import { createApp } from "vue";
createApp(App).use(store).mount('#app')

How to import Library into Vue3 project

could someone help me import a library to my vue3 project so that I can use it in all components?...
I'am trying to import 'moments.js' to my project
Its installed with npm
in my 'main.js' (entry) I import it like:
import { createApp } from "vue"
import App from "./App.vue"
import moment from "moment"
const app = createApp(App)
app.use (moment)
app.mount("#app")
but when I try to console.log(this.moment) from another component I get errors that this.moment is not a function
You can bind moment as a global property on the Vue instance by during the created lifecycle hook in the like manner.
const { createApp } = require('vue');
import App from "./App.vue";
import moment from 'moment';
const MomentPlugin = function (Vue, options) {
Vue.mixin({
created: function () {
this.moment = moment
}
})
}
const app = createApp(App)
app.use(MomentPlugin).mount("#app");
moment function is then available in template context or anywhere the Vue instance is available in scope.
For anyone stumbling onto this post. I changed the code to:
import { createApp } from "vue"
import App from "./App.vue"
import moment from "moment"
const app = createApp(App)
app.provide("moment", moment)
app.mount("#app")
inside other components:
export default {
inject: ["moment"],
// Other code can now use "moment"
}
I would try using this package
https://www.npmjs.com/package/vue-moment
as it is vue-specific. It is a wrapper for moment.
Check the Readme file also for instructions.
https://github.com/brockpetrie/vue-moment
import Vue from 'vue'
import VueMoment from "vue-moment"
Vue.use(VueMoment));
Your case
import { createApp } from "vue"
import App from "./App.vue"
import VueMoment from "vue-moment"
const app = createApp(App)
app.use (VueMoment)
app.mount("#app")
You can use moment like this in any component.
methods: {
moment: function () {
return moment();
}
},
app.use() is for adding Vue plugins to the app. It should be possible to convert Moment.js to a plugin - see "Writing a plugin" in the documentation but it shouldn't be necessary.
You can just import moment.js in any component where you want to use it and the bundling process will make sure that the code is not duplicated anywhere.

How do I access Vue instance inside a js file in Vue3?

In Vue2, I was able to access my Vue instance to make use of components registered with Vue.
test.js
import Vue from 'vue'
export function renderLogin () {
Vue.toasted.show('Please login again', { type: 'error', duration: 2000 })
}
In the above code, I am able to access the toasted package as I have already registered it with Vue in my main.js. However, in Vue3 I'm unable to use the toasted package as I'm unable to access the Vue instance inside a js file.
Need help on how to access Vue instance('this') inside a js file.
After a day of searching, I was able to access the toasted component from the vue instance inside a js file.
First, we would have to export the app instance to be able to read it in a js file
main.js
export const app = createApp({
render() {
return h(AppWrapper);
},
});
Next, we would have to register our component in our globalProperties of our app's instance.
app.config.globalProperties.$toast = toast;
We can now import the app instance in our js file and access toast component
test.js
import { app } from '#/main.js'
app.config.globalProperties.$toast('Toast working fine', {
type: 'success',
duration: 2000,
})
Hope this helps someone out. Please let me know if there are other/better ways. Thank you
// Vue 3 Composition API
<script>
import { getCurrentInstance } from 'vue';
export default {
setup() {
const _instance = getCurrentInstance();
const vueInstance = _instance.appContext;
},
};
</script>
It's not exactly the way as in Vue2, but this will probably expose what you are looking for.
If you want to make a package globally available in Vue3 you probably need to add the following code to a plugin:
//* This will help for accessing the toasted instance in other files (plugins)
app.config.globalProperties.$toasted = toasted;
//* This will expose the toasted instance in components with this.$toasted
app.provide('$toasted', toasted);
With this you are able to get the toasted instance in the options api with: this.$toasted
And with the composition api:
const { $toasted } = _instance.appContext.app.config.globalProperties;
And in another plugin with:
constructor(app) { app.config.globalProperties; }
You can use provider/inject.
For example if you want to use axios across my components, provide axios in your main.js
import { createApp } from "vue";
import App from "./App.vue";
import axios from "axios";
const app = createApp(App);
app.provide("http", axios);
app.mount("#app");
Then in SFC component you could access by 2 ways:
// Composition API
<script>
import { inject } from 'vue'
export default {
setup() {
const http = inject("http");
http.get("https://jsonplaceholder.typicode.com/todos/1").then((response) => {
console.log(response.data);
});
}
}
</script>
// Vue 2 options API
<script>
export default {
inject: ["http"],
}
</script>
Original answer here.

global definition does not work in vue.js

I have tried to import and define a library globally as below, but somehow it does not recognize the global variable.
in main.js,
import Vue from 'vue'
import App from './App'
import router from './router'
import VueJwtDecode from 'vue-jwt-decode'
Vue.config.productionTip = false
Vue.use(VueJwtDecode)
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
components: { App },
template: '<App/>'
})
in Signup.vue,
...
const payload = VueJwtDecode.decode(res.jwt);
...
and the error shows that VueJwtDecode is not defined.
If you are trying to use the named reference of VueJwtDecode, you need to reimport the library in your Signup.vue compoenent since Signup.vue doesn't understand what VueJwtDecode means.
import VueJwtDecode from 'vue-jwt-decode'
const payload = VueJwtDecode.decode(res.jwt);
However, since you have globally installed the library, it has been installed to the Vue instance, meaning that it is available from the this context within your component. As a result, you can also access it from the component context without reimporting:
const payload = this.$jwtDec(res.jwt);
As document, in your component, you need to use
this.$jwtDec(res.jwt)
instead of
VueJwtDecode.decode(res.jwt);

Categories