module namespace not found in mapGetters() Vue Jest Testing Error - vue.js

Currently I am writing a jest testing, but running into the following problem which pops up in my terminal. How can I fix that issue here. According to what the community answered on different forums, I added 'namespaced: true' but without any success. So was wondering what I am doing wrong in this case.
import { shallowMount, createLocalVue } from '#vue/test-utils';
import Vuex from 'vuex';
import Onboarding from '../Onboarding.vue';
const localVue = createLocalVue();
localVue.use(Vuex);
describe('Test onboarding', () => {
let getters;
let store;
const mockStore = { dispatch: jest.fn() };
beforeEach(() => {
getters = {
isReturningUser: () => true,
};
// eslint-disable-next-line import/no-named-as-default-member
store = new Vuex.Store({
namespaced: true,
modules: {
requests: {
getters,
mocks: {
$mockStore: mockStore,
},
},
},
});
});
it('check design with snapshot', () => {
const wrapper = shallowMount(Onboarding, {
store,
localVue,
});
expect(wrapper.findAll('[data-test="onboarding-container"]')).toHaveLength(
1,
);
});
});
<template>
<div
v-if="isReturningUser"
class="popup-container"
data-test="onboarding-container"
>
<div class="popup">
<div class="step">
<img :src="activeStep.image" />
<h2>{{ activeStep.title }}</h2>
<p>{{ activeStep.text }}</p>
</div>
<button
v-if="activeStepIndex <= 2"
class="base-button-primary"
#click="nextStep"
>
Volgende
</button>
<button v-else class="base-button-primary" #click="nextStep">
Ik snap het
</button>
</div>
</div>
</template>
<script>
import { mapGetters } from 'vuex';
export default {
name: 'Onboarding',
data() {
return {
activeStepIndex: 0,
steps: [
{
title: 'Bekijk de drukte',
text: 'Bekijk hoe druk het nu is in de stad.',
image: require('#/assets/images/onboarding/step-1.png'),
},
{
title: 'Pas het scherm aan',
text: "Bezoekers, auto's, parkeerplaatsen, hostspots, routes.",
image: require('#/assets/images/onboarding/step-2.png'),
},
],
};
},
computed: {
...mapGetters('onboarding', ['isReturningUser']),
activeStep() {
return this.steps[this.activeStepIndex];
},
iconUrl() {
return require(`~/assets/icons/checkmark.svg`);
},
},
methods: {
nextStep() {
if (this.activeStepIndex < this.steps.length - 1) {
this.activeStepIndex += 1;
} else {
this.$store.commit('onboarding/isReturningUser', true);
this.activeStepIndex = 0;
}
},
},
};
</script>

Related

Problems with chart.js redrawing or not redrawing graphs

I am Japanese. Therefore, my sentences may be strange. Please keep that in mind.
I am writing code using vue.js, vuex, vue-chart.js and vue-chart.js to display the population of each prefecture of Japan when checked.I’m code is written to redraw the graph when the input element for each prefecture is checked.However, it does not redraw when checked.Also, it may redraw after half of the check.I believe this phenomenon can be confirmed from the following URL.
https://yumemi-coding.web.app/
※There are no errors.
Here's a question: what causes the graphs to redraw or not? Also, how can I code to remedy this?
What I have done to counteract the cause is as follows
I went to the official website and used the rendering process as a reference.
 URL:https://vue-chartjs.org/migration-guides/#new-reactivity-system
 => The way we did it was right.
We thought there was a problem with VueX and coded in a way that did not use it. => There was nothing wrong with vuex.
TopFroont.vue
<template>
<div class="Bar_area">
<Bar :options="chartOptions" :data="chartData" class="Bar_item" />
</div>
</template>
<script>
import { Bar } from "vue-chartjs"
import { Chart as ChartJS, registerables } from "chart.js"
ChartJS.register(...registerables)
export default {
name: "BarChart",
components: { Bar },
data() {
return {
chartOptions: {
responsive: true,
},
}
},
computed: {
chartData() {
return {
labels: this.$store.state.years,
datasets: this.$store.state.prefectures,
}
},
},
}
</script>
NaviBar.vue
<template>
<div class="navApp">
<ul>
<li v-for="(pref, index) in prefData" :key="index" class="pref_itemBox">
<label>
<input type="checkbox" #change="checkItem(pref)" />
<span class="pref_text">{{ pref.prefName }}</span>
</label>
</li>
</ul>
</div>
</template>
<script>
import resasInfo from "#/library/resas.js"
import axios from "axios"
export default {
data() {
return {
resasInfo: resasInfo,
url: resasInfo.url_prefectures,
api: resasInfo.api,
prefData: [],
prefectures: [],
}
},
async created() {
const request_Header = {
headers: { "X-API-KEY": this.api.key },
}
await axios.get(this.url, request_Header).then((res) => {
const value = res.data.result
this.prefData.push(...value)
})
},
methods: {
checkItem(pref) {
// チェックされてる都道府県のみを配列に入れる
const isExistencePref = this.prefectures.indexOf(pref)
isExistencePref === -1
? this.prefectures.push(pref)
: this.prefectures.splice(isExistencePref, 1)
this.$store.dispatch("getPrefectures", this.prefectures)
},
},
}
</script>
vuex => store/index.js
import axios from "axios"
import { createStore } from "vuex"
import createPersistedState from "vuex-persistedstate"
export default createStore({
state: {
prefectures: [],
years: [],
},
mutations: {
getPrefs(state, payload) {
state.prefectures = payload
},
getYears(state, payload) {
state.years = payload
},
},
actions: {
getPrefectures({ commit }, payload) {
// payload => 各都道府県のprefCode + prefName
const allPrefecture_Data = []
const result = payload.map(async (el) => {
const prefCode_data = el.prefCode
axios
.get(
`https://opendata.resas-portal.go.jp/api/v1/population/composition/perYear?prefCode=${prefCode_data}&cityCode=-`,
{
headers: {
"X-API-KEY": "5RDiLdZKag8c3NXpEMb1FcPQEIY3GVwgQwbLqFIx",
},
}
)
.then((res) => {
const value = res.data.result.data[0].data
const TotalPopulation_Year = []
const TotalPopulation_Data = []
// 都道府県の総人口データと年データを各配列に入れ込む
value.forEach((element) => {
TotalPopulation_Data.push(element.value)
TotalPopulation_Year.push(element.year)
})
// rgbaを自動生成する関数 => backgroundColor
const generateRGBA = () => {
const r = Math.floor(Math.random() * 256)
const g = Math.floor(Math.random() * 256)
const b = Math.floor(Math.random() * 256)
const a = 0.8
return `rgba(${r}, ${g}, ${b}, ${a})`
}
// chart.jsに入れ込むデータ
const prefData = {
label: el.prefName,
data: TotalPopulation_Data,
backgroundColor: generateRGBA(),
}
allPrefecture_Data.push(prefData)
commit("getPrefs", allPrefecture_Data)
commit("getYears", TotalPopulation_Year)
})
.catch((err) => {
console.log(err)
})
})
return result
},
},
plugins: [createPersistedState()],
getters: {},
modules: {},
})

Vuex - Unknown action type CreateProfile / ProfileList

everyone. I have a simple project, where I can create a model and make a list of them.
I've taken 2 projects which work correctly separately:
https://github.com/apirobot/django-vue-simplenote
and
https://github.com/jakemcdermott/vue-django-rest-auth
And merged them together with my own backend, which works fine too.
But I get Vuex - Unknown action type CreateProfile (on clicking the submit button) / ProfileList (on refreshing) error.
src/api/profiles.js
import { HTTP } from './common'
export const Profile = {
create (config) {
return HTTP.post('/profiles/', config).then(response => {
return response.data
})
},
delete (profile) {
return HTTP.delete(`/profiles/${profile.id}/`)
},
list () {
return HTTP.get('/profiles/').then(response => {
return response.data
})
}
}
src/components/CreateProfile.vue
/* eslint-disable */
<template lang="pug">
form.form-horizontal(#submit="submitForm")
.form-group
.col-3
label.form-label User
.col-9
input.form-input(type="text" v-model="user" placeholder="Type pk...")
.form-group
.col-3
label.form-label Name
.col-9
input.form-input(type="text" v-model="name" placeholder="Type profile name...")
.form-group
.col-3
label.form-label Phone number
.col-9
textarea.form-input(v-model="phone_number" rows=8 placeholder="Type profile phone number...")
.form-group
.col-3
label.form-label Address
.col-9
textarea.form-input(v-model="address" rows=8 placeholder="Type profile address...")
.form-group
.col-3
.col-9
button.btn.btn-primary(type="submit") Create
</template>
<script>
export default {
name: 'create-profile',
data () {
return {
'user': '',
'name': '',
'phone_number': '',
'address': ''
}
},
methods: {
submitForm (event) {
this.createProfile()
// Т.к. мы уже отправили запрос на создание заметки строчкой выше,
// нам нужно теперь очистить поля title и body
this.user = ''
this.name = ''
this.phone_number = ''
this.address = ''
// preventDefault нужно для того, чтобы страница
// не перезагружалась после нажатия кнопки submit
event.preventDefault()
},
createProfile () {
// Вызываем действие `createNote` из хранилища, которое
// отправит запрос на создание новой заметки к нашему API.
this.$store.dispatch('createProfile', { user: this.user, name: this.name, phone_number: this.phone_number, address: this.address })
}
}
}
</script>
src/components/ProfileList.vue
/* eslint-disable */
<template lang="pug">
#app
.card(v-for="profile in profiles")
.card-header
button.btn.btn-clear.float-right(#click="deleteProfile(profile)")
.card-title {{ profile.name }}
.card-body {{ profile.phone_number }}
.card-body {{ profile.address }}
</template>
<script>
import { mapGetters } from 'vuex'
export default {
name: 'profile-list',
computed: mapGetters(['profiles']),
methods: {
deleteProfile (profile) {
// Вызываем действие `deleteNote` из нашего хранилища, которое
// попытается удалить заметку из нашех базы данных, отправив запрос к API
this.$store.dispatch('deleteProfile', profile)
}
},
beforeMount () {
// Перед тем как загрузить страницу, нам нужно получить список всех
// имеющихся заметок. Для этого мы вызываем действие `getNotes` из
// нашего хранилища
this.$store.dispatch('getProfiles')
}
}
</script>
<style>
header {
margin-top: 50px;
}
</style>
store/index.js
import Vue from 'vue';
import Vuex from 'vuex';
import createLogger from 'vuex/dist/logger';
import auth from './auth';
import password from './password';
import signup from './signup';
import { Profile } from '../api/profiles';
import {
ADD_PROFILE,
REMOVE_PROFILE,
SET_PROFILES
} from './mutation-types.js'
const debug = process.env.NODE_ENV !== 'production';
Vue.use(Vuex);
const state = {
profiles: [] // список заметок
}
// Геттеры
const getters = {
profiles: state => state.profiles // получаем список заметок из состояния
}
// Мутации
const mutations = {
// Добавляем заметку в список
[ADD_PROFILE] (state, profile) {
state.profiles = [profile, ...state.profiles]
},
// Убираем заметку из списка
[REMOVE_PROFILE] (state, { id }) {
state.profiles = state.profiles.filter(profile => {
return profile.id !== id
})
},
// Задаем список заметок
[SET_PROFILES] (state, { profiles }) {
state.profiles = profiles
}
}
// Действия
const actions = {
createProfile ({ commit }, profileData) {
Profile.create(profileData).then(profile => {
commit(ADD_PROFILE, profile)
})
},
deleteProfile ({ commit }, profile) {
Profile.delete(profile).then(response => {
commit(REMOVE_PROFILE, profile)
})
},
getProfiles ({ commit }) {
Profile.list().then(profiles => {
commit(SET_PROFILES, { profiles })
})
}
}
export default new Vuex.Store({
modules: {
auth,
password,
signup,
state,
getters,
actions,
mutations
},
strict: debug,
plugins: debug ? [createLogger()] : [],
});
store/mutation-types.js
export const ADD_PROFILE = 'ADD_PROFILE'
export const REMOVE_PROFILE = 'REMOVE_PROFILE'
export const SET_PROFILES = 'SET_PROFILES'
views/CreateProfile.vue and views/ProfileList.vue same as in src/components.
Thank you.
Your store/index.js file, at the end.
should be something like this.
export default new Vuex.Store({
modules: {
auth,
password,
signup,
},
state,
getters,
actions,
mutations,
strict: debug,
plugins: debug ? [createLogger()] : [],
});
you can think of modules as entirely new store, with their own state, actions, getters, and mutations.

Vue-i18n not translating inside component script tags

Building a language switcher, all works fine but when I use the $t() inside the data object it will not be dynamic when I switch between a language.
Component.vue
<template>
// loop menu here
<div v-for="item in menu">
{{ item.label }}
</div>
</template>
<script>
const mainMenu = [
{
label: $t('dashboard'),
},
{
label: $t('users'),
},
{
label: $t('settings'),
},
}
export default {
data () {
return {
menu = MainMenu
}
}
}
</script>
i18n.js
// https://vue-i18n.intlify.dev/
import { createI18n } from 'vue-i18n'
export function loadLocalMessages () {
const locales = require.context('../locales', true, /[A-Za-z0-9-_,\s]+\.json$/i)
const messages = {}
locales.keys().forEach(key => {
const matched = key.match(/([A-Za-z0-9-_]+)\./i)
if (matched && matched.length > 1) {
const locale = matched[1]
messages[locale] = locales(key)
}
})
return messages;
}
const i18n = createI18n({
locale: 'en',// .env not working
fallbackLocale: 'en',// .env not working
messages: loadLocalMessages(),
});
export default i18n
<template>
<div v-for="item in menu">
{{ item.label }}
</div>
</template>
<script>
export default {
computed: {
menu() {
return [{
label: this.$t('dashboard'),
}, {
label: this.$t('users'),
}, {
label: this.$t('settings'),
}]
}
}
}
</script>
data is only ever called once when creating the component, and it's not intended to be reactive.
To make a property reactive on $t(), it should be computed:
export default {
computed: {
hello() {
return this.$t('hello')
}
}
}
demo

Vue unit tests failing because component method calls this.$route.query - TypeError: Cannot read property 'query' of undefined

Long time user of the wisdom of StackOverflow on and off the job, but first time I'm posting a question. What a milestone!
I'm writing unit tests for a large Vue application, and one of my components uses a method that references $route in order to determine if a query param is being passed in / pass in the param if it is being used. The method calling this.$route.query.article_id works great, however now that I am in testing, the tests don't recognize this.$route.query
I've tried to mock the $route object when using shallowMount to mount my localVue, as described in the doc, but it doesn't work, and I continue to get the same error.
Here is my component:
<template>
<b-container fluid>
<div class="content-page-header"></div>
<b-row>
<b-col cols="3" class="outer-columns text-center" style="color:grey">
<font-awesome-icon
:icon="['fas', 'newspaper']"
class="fa-9x content-page-photo mb-3 circle-icon"
/>
<br />
<br />Get practical tips and helpful
<br />advice in clear articles written
<br />by our staff's experts.
</b-col>
<b-col cols="6" v-if="articlesExist">
<h1 class="header-text">
<b>Articles</b>
</h1>
<div v-if="!selectedArticle">
<div v-for="article in articles">
<article-card :article="article" #clicked="onClickRead" />
<br />
</div>
</div>
<div v-else>
<router-link to="articles" v-on:click.native="setSelectedArticle(null)">
<font-awesome-icon icon="chevron-circle-left" />&nbsp
<b>Back to All Articles</b>
</router-link>
<article-header :article="selectedArticle" />
<br />
<span v-html="selectedArticle.text"></span>
</div>
</b-col>
<b-col cols="6" v-else>
<h1 class="header-text">
<b>Articles</b>
</h1>
<div class="text-center">Stay tuned for more Articles</div>
</b-col>
<b-col class="outer-columns">
<b class="text-color" style="font-size:14pt">Saved Articles</b>
<div v-for="article in userArticles">
<router-link
:to="{path:'articles', query: {article_id: article.article.id}}"
v-on:click.native="setSelectedArticle(article.article)"
>
<user-article :article="article.article" />
</router-link>
<br />
</div>
</b-col>
</b-row>
</b-container>
</template>
<script>
import ArticleCard from "./ArticleCard";
import UserArticle from "./UserArticle";
import ArticleHeader from "./ArticleHeader";
import { library } from "#fortawesome/fontawesome-svg-core";
import {
faNewspaper,
faChevronCircleLeft
} from "#fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "#fortawesome/vue-fontawesome";
library.add(faNewspaper, faChevronCircleLeft);
export default {
name: "Articles",
props: [],
components: {
ArticleCard,
ArticleHeader,
UserArticle,
library,
FontAwesomeIcon,
faNewspaper,
faChevronCircleLeft
},
mixins: [],
data() {
return {
selectedArticle: null
};
},
computed: {
articles() {
return this.$store.getters.articles.filter(article => article.text);
},
articlesExist() {
return Array.isArray(this.articles) && this.articles.length;
},
userArticles() {
return this.$store.getters.userArticles;
},
articleParam() {
return parseInt(this.$route.query.article_id);
}
},
methods: {
setSelectedArticle(article) {
this.selectedArticle = article;
},
onClickRead(article) {
this.selectedArticle = article;
}
},
mounted() {
if (this.articleParam) {
this.setSelectedArticle(
this.articles.filter(article => article.id === this.articleParam)[0]
);
}
}
};
</script>
<style lang="stylus" scoped>
.text-color {
color: #549DB0;
}
.header-text {
color: white;
margin-top: -50px;
margin-bottom: 20px;
}
.outer-columns {
background-color: #F2FBFD;
padding-top: 20px;
}
.nav-back {
color: #549DB0;
background-color: #F0FBFD;
padding: 5px;
}
</style>
And here is my test:
import { shallowMount, createLocalVue } from '#vue/test-utils'
import VueRouter from 'vue-router'
import Articles from '../../../app/javascript/components/member-dashboard/Articles.vue'
const localVue = createLocalVue()
localVue.use(Vuex)
localVue.use(VueRouter)
localVue.use(BootstrapVue)
describe('Articles', () => {
let store
let getters
let state = {
articles: [
{
title: "Testing Vue Components"
},
{
title: "This One shows",
text: "<p>You can see me!</p>"
},
{
title: "Another One",
text: "<p>See me too!</p>"
}
],
userArticles: [
{article: {
title: "This One shows",
text: "<p>You can see me!</p>"
}},
{article: {
title: "Another One",
text: "<p>See me too!</p>"
}}
]
}
beforeEach(() => {
getters = {
articles: () => {
return state.articles
},
userArticles: () => {
return state.userArticles
}
}
store = new Vuex.Store({ getters })
})
it('only displays article with body text', () => {
const wrapper = shallowMount(Articles, {
store,
localVue
})
expect(wrapper.vm.articles.length).to.deep.equal(2)
})
})
As I mentioned, in the shallow mount, I've tried doing this:
const wrapper = shallowMount(Articles, {
store,
localVue,
mocks: {
$route: {
query: null
}
}
})
But I continue to get this error:
TypeError: Cannot read property 'query' of undefined
at VueComponent.articleParam (webpack-internal:///1:107:35)
When I remove the line return parseInt(this.$route.query.article_id); from the articleParam method, my test passes.
How do I get around this call to this.$route.query in the component? It's not necessary to my test, but is causing my test to fail when mounting the component.
import import VueRouter from 'vue-router'; in your unite test file and create a new object of the router like const router = new VueRouter(); and use it in your test case.
I have updated code here:
import { shallowMount, createLocalVue } from '#vue/test-utils'
import VueRouter from 'vue-router'
import Articles from '../../../app/javascript/components/member-dashboard/Articles.vue'
const localVue = createLocalVue()
localVue.use(Vuex)
localVue.use(VueRouter)
localVue.use(BootstrapVue);
const router = new VueRouter();
describe('Articles', () => {
let store
let getters
let state = {
articles: [
{
title: "Testing Vue Components"
},
{
title: "This One shows",
text: "<p>You can see me!</p>"
},
{
title: "Another One",
text: "<p>See me too!</p>"
}
],
userArticles: [
{article: {
title: "This One shows",
text: "<p>You can see me!</p>"
}},
{article: {
title: "Another One",
text: "<p>See me too!</p>"
}}
]
}
beforeEach(() => {
getters = {
articles: () => {
return state.articles
},
userArticles: () => {
return state.userArticles
}
}
store = new Vuex.Store({ getters })
})
it('only displays article with body text', () => {
const wrapper = shallowMount(Articles, {
store,
router,
localVue
})
expect(wrapper.vm.articles.length).to.deep.equal(2)
})
})

Pre-fetch data using vuex and vue-resource

I'm building an app following this structure: http://vuex.vuejs.org/en/structure.html
My components/App.vue like this:
<template>
<div id="app">
<course :courses="courses"></course>
</div>
</template>
<script>
import Course from './course.vue'
import { addCourses } from '../vuex/actions'
export default {
vuex: {
getters: {
courses: state => state.courses,
},
actions: {
addCourses,
}
},
ready() {
this.addCourses(this.fetchCourses())
},
components: { Course },
methods: {
fetchCourses() {
// what do I have to do here
}
}
}
</script>
How can I fetch the data and set it to the state.courses ?
Thanks
I've just figured it out:
in /components/App.vue ready function, I just call:
ready() {
this.addCourses()
},
in vuex/actions.js:
import Vue from 'vue'
export const addCourses = ({ dispatch }) => {
Vue.http.get('/api/v1/courses')
.then(response => {
let courses = response.json()
courses.map(course => {
course.checked = false
return course
})
dispatch('ADD_COURSES', courses)
})
}
and in vuex/store.js:
const mutations = {
ADD_COURSES (state, courses) {
state.courses = courses
}
}