Problem with handling Browser Cookies in vue.js - vue.js

Well, I have a problem with a plugin handling browser cookies in vue.js
I installed plugin and imported it in main.js. I configured it too like below:
import { createApp } from "vue";
import { createPinia } from "pinia";
import { globalCookiesConfig } from "vue3-cookies";
import App from "./App.vue";
import router from "./router";
import { useMainStore } from "#/stores/main.js";
import { useStyleStore } from "#/stores/style.js";
import { darkModeKey, styleKey } from "#/config.js";
import "./css/main.css";
globalCookiesConfig({
expireTimes: "30d",
path: "/",
domain: "",
secure: true,
sameSite: "None",
});
/* Init Pinia */
const pinia = createPinia();
/* Create Vue app */
createApp(App).use(router).use(pinia).mount("#app");
/* Init Pinia stores */
const mainStore = useMainStore(pinia);
const styleStore = useStyleStore(pinia);
/* Fetch sample data */
mainStore.fetch("clients");
mainStore.fetch("history");
/* App style */
styleStore.setStyle(localStorage[styleKey] ?? "basic");
/* Dark mode */
if (
(!localStorage[darkModeKey] &&
window.matchMedia("(prefers-color-scheme: dark)").matches) ||
localStorage[darkModeKey] === "1"
) {
styleStore.setDarkMode(true);
}
/* Default title tag */
const defaultDocumentTitle = "SOFT-KOMP";
/* Set document title from route meta */
router.afterEach((to) => {
document.title = to.meta?.title
? `${to.meta.title} — ${defaultDocumentTitle}`
: defaultDocumentTitle;
});
And In my LoginView.vue i inserted this line in function:
this.$cookies.set('hover-time','1s');
Unfortunately I receive this:
runtime-core.esm-bundler.js:218 Uncaught TypeError: Cannot read properties of undefined (reading '$cookies')
So this is my LoginView.vue file. And I tried to test some of other sessions and cookie plugins but results was always the same. PRobably Im not installing this plugin properly in the component or something?
<script setup>
import { reactive } from "vue";
import { useRouter } from "vue-router";
import { mdiAccount, mdiAsterisk } from "#mdi/js";
import SectionFullScreen from "#/components/SectionFullScreen.vue";
import CardBox from "#/components/CardBox.vue";
import FormCheckRadio from "#/components/FormCheckRadio.vue";
import FormField from "#/components/FormField.vue";
import FormControl from "#/components/FormControl.vue";
import BaseButton from "#/components/BaseButton.vue";
import BaseButtons from "#/components/BaseButtons.vue";
import LayoutGuest from "#/layouts/LayoutGuest.vue";
const form = reactive({
login: "john.doe",
pass: "highly-secure-password-fYjUw-",
remember: true,
});
const router = useRouter();
const submit = () => {
router.push("/dashboard");
};
const login = () =>{
this.$cookies.set('hover-time','1s');
}
</script>
<template>
<LayoutGuest>
<SectionFullScreen v-slot="{ cardClass }" bg="purplePink">
<CardBox :class="cardClass" is-form #submit.prevent="submit">
<FormField label="Login" help="Please enter your login">
<FormControl
v-model="form.login"
:icon="mdiAccount"
name="login"
autocomplete="username"
/>
</FormField>
<FormField label="Password" help="Please enter your password">
<FormControl
v-model="form.pass"
:icon="mdiAsterisk"
type="password"
name="password"
autocomplete="current-password"
/>
</FormField>
<FormCheckRadio
v-model="form.remember"
name="remember"
label="Remember"
:input-value="true"
/>
<template #footer>
<BaseButtons>
<BaseButton #click='login' type="submit" color="info" label="Login" />
<BaseButton to="/dashboard" color="info" outline label="Back" />
</BaseButtons>
</template>
</CardBox>
</SectionFullScreen>
</LayoutGuest>
</template>

Well, what you have to is, first import the use in your setup in your LoginView.vue:
import { useCookies } from "vue3-cookies";
Then you can use it like this:
const { cookies } = useCookies();
and then cookies is accessible across your setup.
See answer and comment if you need more info.

Related

Cypress component testing with Vue-I18N

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?

Vue3.js and Pinia: Why is my store state undefined in VueDevtools?

I am using Vue 3 with Pinia ^2.0.14. I'm importing Pinia into the app in main.ts like so:
import { createPinia } from 'pinia'
import { createApp } from 'vue'
import App from './App.vue'
const pinia = createPinia()
const app = createApp(App).use(pinia).mount('#app')
I'm creating the store language.ts like so:
import { defineStore } from 'pinia'
export const useLanguageStore = defineStore({
id: 'language',
state: () => ({
language: 'English',
languages: ['English', 'Spanish'],
}),
})
and using it in LanguageDropdown.vue like so:
<script setup lang="ts">
import { useLanguageStore } from '#/store/language.ts'
const languageStore = useLanguageStore()
</script>
<template>
<select
v-model="languageStore.language">
<option
v-for="language in languageStore.languages"
:key="language"
:value="language"
>
{{ language }}
</option>
</select>
</template>
The code works as expected, but in the Vue devtools inspector languageStore.language, languageStore.languages, and language.state are undefined. Why would that be?
screen shot of Vue devtools inspector
I found a away to solve it, though I think its not perfect
// should use computed
const languages = computed(() => languageStore.languages )

Vue.js with pinia npm server error variable is assigned a value but never used

I am using this video https://www.youtube.com/watch?v=Ok6vO98RV_Q&t=108s, but I keep getting the error: "error 'counter' is assigned a value but never used no-unused-vars". What am I missing?
<template>
{{ counter }}
</template>
<script>
import { storeToRefs } from "pinia";
import useStore from "../store/useStore";
const main = useStore;
const { counter } = storeToRefs(main);
</script>
store:
import { defineStore } from "pinia";
// import { auth } from "./auth.module";
export const useStore = defineStore("main", {
state: () => ({
counter: null,
}),
});
You have to specify <script setup> for Composition API or else you have to expose the properties manually to the template by returning them.
<template>
{{ counter }}
</template>
<script setup>
import { storeToRefs } from "pinia";
import useStore from "../store/useStore";
const main = useStore();
const { counter } = storeToRefs(main);
</script>

Testing with vitest and testing-library is not working: it is due to using the SFC Script Setup?

I'm new to Vue and especially with the composition functions. I'm trying to test a component that uses the script setup; however, it seems that it is not working.
The component is this one:
<template>
<el-card class="box-card" body-style="padding: 38px; text-align: center;" v-loading="loading">
<h3>Login</h3>
<hr class="container--separator">
<el-form ref="formRef"
:model="form"
>
<el-form-item label="Username">
<el-input v-model="form.username" placeholder="Username"/>
</el-form-item>
<el-form-item label="Password">
<el-input type="password" v-model="form.password" placeholder="Password" />
</el-form-item>
<el-button color="#2274A5" v-on:click="submitForm()">Login</el-button>
</el-form>
</el-card>
</template>
<script lang="ts" setup>
import {reactive, ref} from 'vue'
import { useRouter } from 'vue-router'
import type {FormInstance} from 'element-plus'
import {useMainStore} from "../../stores/index"
import notification from "#/utils/notification"
import type User from "#/types/User"
const formRef = ref<FormInstance>()
const form: User = reactive({
username: "",
password: "",
})
const router = useRouter()
const loading = ref(false)
const submitForm = (async() => {
const store = useMainStore()
if (form.username === "") {
return notification("The username is empty, please fill the field")
}
if (form.password === "") {
return notification("The password is empty, please fill the field")
}
loading.value = true;
await store.fetchUser(form.username, form.password);
loading.value = false;
router.push({ name: "home" })
})
</script>
<style lang="sass" scoped>
#import "./LoginCard.scss"
</style>
When I try to test it:
import { test } from 'vitest'
import {render, fireEvent} from '#testing-library/vue'
import { useRouter } from 'vue-router'
import LoginCard from '../LoginCard/LoginCard.vue'
test('login works', async () => {
render(LoginCard)
})
I had more lines but just testing to render the component gives me this error.
TypeError: Cannot read properties of undefined (reading 'deep')
❯ Module.withDirectives node_modules/#vue/runtime-core/dist/runtime-core.cjs.js:3720:17
❯ Proxy._sfc_render src/components/LoginCard/LoginCard.vue:53:32
51| loading.value = false;
52|
53| router.push({ name: "home" });
I tried to comment parts of the component to see if it was an issue with a specific line (the router for example), but the problem seems to continue.
I tried to search about it but I don't know what I'm doing wrong, it is related to the component itself? Should I change how I've done the component?
I had the same issue, and was finally able to figure it out. Maybe this will help you.
The problem was I had to register global plugins used by my component when calling the render function.
I was trying to test a component that used a directive registered by a global plugin. In my case, it was maska, and I used the directive in a input that was rendered somewhere deeply nested inside my component, like so:
<!-- a global directive my component used -->
<input v-maska="myMask" .../>
#vue/test-utils didn't recognize it automatically, which caused the issue. To solve it, I had to pass the used plugin in a configuration parameter of the render() function:
import Maska from 'maska';
render(MyComponent, {
global: {
plugins: [Maska]
}
})
Then, the issue was gone. You can find more info about render()
configuration here:
https://test-utils.vuejs.org/api/#global

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')
}