Cypress component testing with Vue-I18N - vue.js

I am trying to use Cypress for component testing in my Vue app. I am using the vue-i18n library to provide localisation for the app. When attempting to test the rendering of my loading spinner component, I am getting the following error from the vue-i18n library:
SyntaxError: Need to install with `app.use` function
at createCompileError (http://localhost:5173/__cypress/src/node_modules/.vite/deps/vue-i18n.js?v=64756eb2:183:17)
at createI18nError (http://localhost:5173/__cypress/src/node_modules/.vite/deps/vue-i18n.js?v=64756eb2:2625:10)
at useI18n (http://localhost:5173/__cypress/src/node_modules/.vite/deps/vue-i18n.js?v=64756eb2:4231:11)
Previously to this, I was getting an error from Pinia. I resolved this by adding the following to cypress/support/component.ts:
import { createPinia, setActivePinia } from 'pinia';
setActivePinia(
createPinia()
);
My LoadingSpinner component code is as follows:
<script setup lang="ts">
import { computed } from "#vue/reactivity";
import { useLocaleStore } from "#/stores/locale";
//props
const { i18n } = useLocaleStore();
</script>
<template>
<div class="d-flex justify-content-center m-5">
<div
class="spinner-border text-primary"
:style="{ width, height }"
role="status"
>
<span class="visually-hidden">{{ i18n.t("loading") }}</span>
</div>
</div>
</template>
And the test code:
import LoadingSpinner from "../../src/components/ui/LoadingSpinner.vue";
describe("LoadingSpinner", () => {
it("renders", () => {
cy.mount(LoadingSpinner);
});
});
/stores/locale:
import { computed } from "vue";
import { defineStore } from "pinia";
import { useI18n } from "vue-i18n";
export const useLocaleStore = defineStore("locale", () => {
const i18n = useI18n({
useScope: "global",
inheritLocale: true,
});
const currentLocale = computed(() => i18n.locale);
const locales = computed(() => i18n.availableLocales);
return { i18n, currentLocale, locales };
});
I found this github release that implies I need to add vue-i18n as a plugin to the mount() call, but I can't work out how to do it. Does anyone know a solution?

Related

Vue.js - Element Plus - How to test el-dropdown component

I have a problem that I can't trigger el-dropdown menu. I've followed the testing approach done in element-plus repository but couldn't able to simulate mouseenter event and see whether dropdown menu is opened.
my code can be found below.
<template>
<el-dropdown>
<el-icon>
<MoreFilled/>
</el-icon>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item>Send a message</el-dropdown-item>
<el-dropdown-item>Report</el-dropdown-item>
<el-dropdown-item>Block</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</template>
<script lang="ts" setup>
import { MoreFilled } from '#element-plus/icons-vue';
</script>
and my test code can be found here
import { mount } from "#vue/test-utils";
import { nextTick } from "vue";
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
import EntryCardFooterDropdown from "../EntryCardFooterDropdown.vue";
import { ElTooltip } from "element-plus";
describe('EntryCardFooterDropdown', () => {
it('render', async () => {
const wrapper = mount(EntryCardFooterDropdown)
await nextTick()
const content = wrapper.findComponent(ElTooltip).vm as InstanceType<typeof ElTooltip>
vi.useFakeTimers();
const triggerElm = wrapper.find('.el-tooltip__trigger');
expect(content.open).toBe(false);
await triggerElm.trigger('mouseenter');
vi.runAllTimers();
expect(content.open).toBe(true);
})
})

Using Pinia with Vue.js Web components

Question has been updated
I'm trying to use a Pinia's store with web components created with Vue.js but I have this error in the console:
[Vue warn]: injection "Symbol(pinia)" not found at <HelloWorld.ce msg="message" >
I have a dead simple exemple.
main.ts
import { defineCustomElement } from 'vue'
import HelloWorld from './components/HelloWorld.ce.vue'
const ExampleElement = defineCustomElement(HelloWorld)
customElements.define('hello-world', ExampleElement)
store.ts
import { defineStore, createPinia, setActivePinia } from "pinia";
setActivePinia(createPinia());
export const useCounterStore = defineStore('counter', {
state: () => ({
counter: 0,
}),
actions: {
increment() {
this.counter++;
},
},
});
HelloWorld.ce.vue
<script setup lang="ts">
import { ref } from 'vue'
import { useCounterStore } from '../store.ts'
defineProps<{ msg: string }>()
const store = useCounterStore()
</script>
<template>
<h1>{{ msg }}</h1>
<div class="card">
<button type="button" #click="store.increment()">count is {{ store.counter }}</button>
</div>
</template>
You're recreating pinia in the store after already creating it in main.js. Remove these lines from your store:
import { createPinia } from 'pinia'
const pinia = createPinia()

WebGL Earth or globe.gl in VUE.JS

Can I use "WebGL Earth" or "globe.gl" in vue.js? I search a lot but what I found was that there is "react-globe.gl" for react developers, but can't find the same for vue.
If I can use any of them in vue, how can I import and initialize it?
I am currently am using globe.gl with vue 3, got it running like this.
Can also checkout a template repo I have https://github.com/GBerghoff/Globe.gl-with-Vue-3
<template>
<div ref="globeDiv"></div>
</template>
<script>
import Globe from "globe.gl";
import { ref, onMounted } from "vue";
export default {
setup() {
const globeDiv = ref(null);
onMounted(() => {
const myGlobe = Globe();
myGlobe(globeDiv.value).globeImageUrl(
"//unpkg.com/three-globe/example/img/earth-night.jpg"
);
});
return {
globeDiv,
};
},
};
</script>

Vue Snapshot testing with RouterLinkStub does not render anchor as expected

I'm trying to run jest-snapshot tests in a Vue application which has a Component with router-link element.
I'm using RouterLinkStub as recommended in Snapshot Testing with Vue-Router
My component template is something like
<template>
<section class="content">
<div>
<router-link to="/" v-slot="{ href, route, navigate }">
<a :href="href" #click="navigate">
Homepage
</a>
</router-link>
</div>
</section>
</template>
The problem is that in snapshot, the router-link renders as empty anchor element (<a />), so if I change the link text the snapshot test stills pass.
This is my snapshot test:
import { RouterLinkStub, shallowMount } from '#vue/test-utils'
import PageNotFound from 'views/PageNotFound.vue'
describe('PageNotFound.vue', () => {
it('renders as expected', () => {
const wrapper = shallowMount(PageNotFound, {
stubs: {
RouterLink: RouterLinkStub
}
})
expect(wrapper.element).toMatchSnapshot()
})
})
Can you help me please?
Thanks
With Vue 3
import { shallowMount, RouterLinkStub } from '#vue/test-utils'
import router from '../../router'
const findComponentRouterLink = () =>
wrapper.findComponent(RouterLinkStub)
const wrapper = shallowMount(Component, {
global: {
plugins: [router],
stubs: {
RouterLink: RouterLinkStub,
},
},
})
findComponentRouterLink().text()).toBe('Link text')

Vue components not loading

I was given this problematic codebase, where the Vue components aren't loading in.
Vue is mounting, but without any components.
This is a Laravel 5.7 app, using blade templates with some Vue added in.
This is the initial code:
import 'babel-polyfill'
import loadClientScripts from './load-client-scripts'
import 'bootstrap-material-design/js/'
// Vue & axios
import Vue from 'vue'
import { axios } from '../axios-config'
import BootstrapVue from 'bootstrap-vue/dist/bootstrap-vue.esm'
import { createLocales } from '../vue-i18n-config'
import Noty from 'noty'
//Components
import signInForm from './components/SignInForm'
import signUpForm from './components/SignUpForm'
import forgotPassForm from './components/ForgotPassForm'
// import RegisterToAgency from './components/RegisterToAgency'
import SendEmailForm from './components/SendEmailForm'
import AgencyServiceCategories from './components/AgencyServiceCategories'
import DropdownWithCheckboxes from './components/DropdownWithCheckboxes'
import LasiCoalitionAgencies from './components/LasiCoalitionAgencies'
import ServiceProviders from "./components/ServiceProviders";
import ServiceProvider from "./components/ServiceProvider";
import vSelect from "vue-select";
window.axios = axios
Vue.component('v-select', vSelect)
// Bootstrap Vue
Vue.use(BootstrapVue)
export function createApp() {
const i18n = createLocales(window.locale)
// Components
Vue.component('sign-in-form', signInForm)
Vue.component('sign-up-form', signUpForm)
Vue.component('forgot-pass-form', forgotPassForm)
// Vue.component('register-to-agency', RegisterToAgency)
Vue.component('send-email-form', SendEmailForm)
Vue.component('agency-service-categories', AgencyServiceCategories)
Vue.component('dropdown-with-checkboxes', DropdownWithCheckboxes)
Vue.component('lasi-coalition-agencies', LasiCoalitionAgencies)
Vue.component('service-providers', ServiceProviders)
Vue.component('service-provider', ServiceProvider)
new Vue({
i18n
}).$mount('#app')
}
sign in form component for example:
<template>
<div>
<b-form
id="sign-in-form"
#submit="onSubmit"
>
<div class="form-group">
<b-form-input
id="sgi-email"
v-model="model.email"
required
name="email"
:state="state('email')"
type="email"
:placeholder="$t('validation.attributes.email_address')"
/>
<b-form-feedback>{{ feedback('email') }}</b-form-feedback>
</div>
<div class="form-group mb-3">
<b-form-input
id="sgi-password"
v-model="model.password"
required="required"
name="password"
:state="state('password')"
type="password"
:placeholder="$t('validation.attributes.password')"
/>
<b-form-feedback>{{ feedback('password') }}</b-form-feedback>
</div>
<div class="form-group my-0">
<a
class="text-opacity forgot-pass-link"
href="#"
>
{{ $t('labels.user.password_forgot') }}
</a>
</div>
</b-form>
</div>
</template>
<script>
console.log('IM HIT')
export default {
name: 'SignInForm',
data() {
return {
model: {
email: '',
password: ''
},
validation: {}
}
},
mounted() {
this.test()
},
methods: {
test() {
console.log("test")
},
feedback(name) {
if (this.state(name)) {
return this.validation.errors[name][0]
}
},
state(name) {
return this.validation.errors !== undefined &&
this.validation.errors.hasOwnProperty(name)
? 'invalid'
: null
},
onSubmit(evt) {
evt.preventDefault()
window.axios
.post('/login', this.model)
.then(response => {
location.href = '/app'
})
.catch(e => {
if (e.response.status === 422) {
this.validation = e.response.data
return
}
})
}
}
}
</script>
Any ideas help!
in the example sign in form, the console does output the "Im hit" that I had placed to ensure that things were loaded.
Thanks
Are you at any point rendering anything with that Vue instance?
Try passing a component to its render function like so:
// lets pretend you've imported a component from some file App.vue up here and called the component simply 'App'
// e.g.: const App = require('./App.vue') or import App from './App.vue';
Vue.use(BootstrapVue)
export function createApp() {
const i18n = createLocales(window.locale)
// Components
Vue.component('sign-in-form', signInForm)
Vue.component('sign-up-form', signUpForm)
Vue.component('forgot-pass-form', forgotPassForm)
// Vue.component('register-to-agency', RegisterToAgency)
Vue.component('send-email-form', SendEmailForm)
Vue.component('agency-service-categories', AgencyServiceCategories)
Vue.component('dropdown-with-checkboxes', DropdownWithCheckboxes)
Vue.component('lasi-coalition-agencies', LasiCoalitionAgencies)
Vue.component('service-providers', ServiceProviders)
Vue.component('service-provider', ServiceProvider)
new Vue({
i18n,
render: createElement => createElement(App) // needs a render function
}).$mount('#app')
}