Vue-test-utils using mixin for vee-validate in nuxt - vue.js

I'm trying to test if validation works for a form with vee-validate and vue-test-utils. I also use nuxt and have created a custom plugin which install vee-validate and provides two custom computed properties as a mixin.
The problem is that I need a way to use these mixins within a localVue instance, however, I cannot just import the whole file as it results in vee-validate being installed two times on the main vue instance. I also cannot just say localVue.use(MyCustomVeeValidatePlugin) because the plugin doesn't have an install method ("plugins" in nuxt are somewhat different than in vue).
What works is creating a file which exports isFormValid and isFormChanged and then have the plugin import these methods. Then I also need to import these methods in the test file and create a mixin for the localVue instance. I would much rather prefer defining the mixin in a single plugin file. I know this is very specific but has anyone had a similar problem? I could imagine rewriting the plugin to be more like it is defined in the Vue.js docs (with an install method) and install it somehow.
Plugin:
import Vue from "vue";
import VeeValidate from "vee-validate";
Vue.use(VeeValidate);
//create global mixin for checking if form is valid
//note: every input element needs a name
Vue.mixin({
computed: {
isFormValid() {
return Object.keys(this.fields).every(key =>
this.fields[key].valid);
},
isFormChanged() {
return Object.keys(this.fields).some(key =>
this.fields[key].changed);
}
}
});

As far as I know, based on the recommendations I read in "Testing VueJs Applications (https://www.manning.com/books/testing-vue-js-applications), the author, who is also the main author of the vue-test-utils recommends:
I’ve already spoken about why you should use a localVue constructor and avoid installing on the base constructor. This is especially important for Vue Router. Always use a localVue to install Vue Router in tests. You must make sure that no file in your test suite imports a file that calls Vue.use with Vue Router. It’s easy to accidentally import a file that includes Vue.use. Even if you don’t run a module, if the module is imported, then the code inside it will be evaluated.
Based on that recommendation, I moved Vue.use() calls out of files like store.js and router.js and into main.js, which isn't used during testing.

Related

vue-router useRouter doesn't work when building library components

I am building a Vue3 npm component library with hopes that I could access the current router with vue-router's useRouter, and it would be automatically provided by whatever vue app is importing my library components.
If my library components are referenced directly import myCompThatUsesRouter from '../../myCompThatUsesRouter.vue the router works.
If I reference the same component via the node_module package import myCompThatUsesRouter from '#myPackage' router is undefined.
I also get a vue warning
injection "Symbol()" not found.
Is this not how these inject methods are intended to work?
The issue was that my library defined vue-router as a "dependency", not a "peerDependency".
https://nodejs.org/es/blog/npm/peer-dependencies/
Also my vite config needed to define vue-router as "external"
vite.config.ts
rollupOption: {
external: ['vue', 'vue-touer']
}

Add npm modules to Piranha CMS

I'm new to extending the Piranha CMS on the frontend side of things and I'm wanting to add more functionality to the manager pages using some libraries from npm. The CMS is using Vue, which has some helpful libraries available. I want to use
import { SomeLibrary } from "some-library"
as well as the corresponding Vue components that the library comes with (ie, <some-library/>). However, I'm unable to use them due to the way Piranha accepts custom scripts. The scripts are added into the project via
App.Modules.Manager().Scripts.Add("~/assets/js/myscripts.js");
which does not leave room for me to use modules for my javascript, as far as I know. Is there some way around this so I can use npm libraries?
More info added, here is an example of what I am trying to do and why it does not work. The file test.js is added to the project in Startup.cs with the above line.
test.js:
import draggable from 'vuedraggable'
export default {
components: {
draggable,
},
data() {
return {}
},
created() {
console.log("I am a test");
}
}
Navigating to any page in the project results in the error Uncaught SyntaxError: Cannot use import statement outside a module. I cannot add type="module" to the Startup.cs because there are no parameters to do this., and I'm not sure if that would even solve the problem. I've only ever worked with javascript using modules so I'm unsure what's going on here. Am I missing something obvious? How can you use any npm libraries (which are modules) in the project otherwise?

Global Components in Nuxt JS

I have built some components such as modals and reviews that I want to use and reuse just about everywhere in my site.
I have made a single global.js file in the plugins folder, So iv'e just registered this in my nuxt.config.js file once.
In my global.js file i've imported vue and called the components
import Vue from 'vue';
Vue.component('component-modal', () => import('#/components/modal'));
Vue.component('component-modal-other', () => import('#/components/modalOther'));
Vue.component('component-reviews', () => import('#/components/reviews'));
The reason for this is that now I don't have to do component import on every instance that I want to use it for I just call the component <my-component>.
However I've just switched to Universal mode and i'm now getting hydration warnings that are all coming from my global.js components file that I made in my plugins folder.
Should I not have a global.js file and each component that I want to use globally have it's own file in the plugins folder? Or do I just need to import the component when I need it locally in the file that is requiring it?
What is the best way to re-use and register global mini components that I have created?
Vue Warning
The client-side rendered virtual DOM tree is not matching server-rendered content. This is likely caused by incorrect HTML markup, for example nesting block-level elements inside <p>, or missing <tbody>. Bailing hydration and performing full client-side render
The only way I can avoid this warning is not to put my components into a global.js component file and register it with Nuxt plugins. Only use the component import into each instance which kind of defeats the purpose of global components
try this
import Modal from '#/components/modal'
Vue.component('Modal', Modal)
in your view
</modal>

How to have a Nuxt.js and a bare Vue.js project share components

I want to create a website with two separate applications which share some components and store.
index is the public application where I want to use nuxt.js to have SSR.
admin should be a classic SPA where SSR is not needed.
My first idea was to create a multi-page vue application as described in the vue-cli docs at https://cli.vuejs.org/config/#pages
However I'm unsure if this feature fits my needs and if it's possible/advisable to have a nuxt.js app alongside a bare vue.js application, because nuxt.js has a different project structure.
Is there any way to configure nuxt.js so it fits in the default project structure of vue or to configure vue to use the nuxt.js folder structure?
Create multiple Vue Applications with (some) shared source-files (components/store/mixins/etc)
It is easily possible to share resources across multiple Vue-Apps simply by importing the respective resource everywhere you would like to use it, e.g.:
// in /components/MyComponent.vue
<template>
<div>I'm a shared component</div>
<template>
// in /user-app/entry.js
import MyComponent from '../components/MyComponent';
Vue.component('MyComponent', MyComponent);
new Vue({...})
// in /admin-app/entry.js
import MyComponent from '../components/MyComponent';
Vue.component('MyComponent', MyComponent);
new Vue({...})
Where it becomes a little bit complicated
To actually create the seperate apps you will have to use some built-process. By far the most common tool to build Vue apps (and the one used by VueCLI & Nuxt) is WebPack.
To create multiple apps with WebPack you need to do one of two things:
simply use the integreated build-processes of the VueCLI and Nuxt separately. It will work out of the box.
create your own WebPack configuration & the EntryPoint of every single app in WebPack's configuration. NOTE: It is not trivial to use your own build-process for Nuxt, if you really want to use Nuxt I advice you against it. Run with two seperate build-processes instead.
The WebPack configuration itself is a JavaScript Object. The relevant key to declare your EntryPoints is sensibly called entry. Here you specify the name of your EntryPoint and the corresponding path (the path to the entry-file).
The 'Pages' feature of the VueCLI uses this under the hood. However, I believe it is very well worth it to learn how to use WebPack yourself. It is not that complex and will significantly benefit most or all of your JavaScript projects.
A basic example configuration could look like this:
// in webpack.config.js
module.exports = {
mode: 'development',
entry: {
admin: path.resolve(__dirname, './admin-app.js'),
user path.resolve(__dirname, './user-app.js'),
},
// other config
}
WebPack is very well documented: https://webpack.js.org/concepts/

Can I use a component that uses VueX without my app also using VueX?

I would like to use this component in my Vue app: https://commbocc.github.io/hcflgov-vue-esri-search/docs/
However it is throwing an error: TypeError: "t.$store is undefined"
I suspect the cause is that I am not using VueX, so I'm failing to initialise a VueX store that the component is looking for.
In general, is it possible to use a component that uses VueX if my app does not? Is there some way around this? I think the effort involved in incorporating VueX would be too high.
Yes you can. In most cases, people create vue-plugins. When doing that, you can install them like npm install myCoolPlugin and use them in your app by importing it: import myCoolPlugin from 'myCoolPlugin' and then depending of the plugin, you can either globally install it like vuex does:
Vue.use(myCoolPlugin)
or you can explicitly use the plugin's components as you wish directly where you are going to use them like so:
import {coolButton, coolInput} from 'myCoolPlugin';
export default {
name: 'home-page',
components: [coolButton, coolInput],
...
}
Plugins also have a package.json file that holds metadata about what that plugin depends on etc (just like your app). When you npm i myCoolPlugin, npm checks the plugin's package.json file to see what 3rd party packages the plugin depends on and then continues to install them in your app's node_modules.
The issue with your component "esri-search" is that it is not set up as a package/plugin. Therefore it will not install any dependencies it needs (like vuex, lodash etc...) in your app.
This is why you had to install vuex as a dependency to your app, because when you copy paste this component in to your app, it is not a plugin, it becomes your app.
Does this make sense? :)
It seems in this case I was able to solve that error like this:
npm install --save vuex
In main.js:
import VueX from 'vuex';
Vue.use(VueX);
This doesn't fully answer whether or not this would always be the case though, so open to better answers.