Vuetify icons gives an error with storybook - vue.js

Hello everyone I'm facing an issue When I use the storybook with vuetify framework and when I run the project it gives the following error multiple times.
Cannot read properties of undefined (reading 'component')
TypeError: Cannot read properties of undefined (reading 'component')
at remapInternalIcon (http://localhost:6006/vendors~main.f3adcb00877b75c26c76.bundle.js:267378:37)
at VueComponent.getIcon
and here is I'm using storybook v6.1.11 npm packages
"#storybook/addon-essentials": "^6.5.16",
"#storybook/addon-actions": "^6.1.11",
"#storybook/addon-controls": "^6.5.16",
"#storybook/addon-docs": "^6.1.11",
"#storybook/addon-links": "^6.1.11",
"#storybook/addons": "^6.1.11",
"#storybook/preset-scss": "^1.0.3",
"#storybook/vue": "^6.1.11",
"vuetify-loader": "^1.7.0",
"vuetify": "^2.6.0"
knowing that the my project is working fine and all of the components renders correctly even v-icon
main.js
const path = require('path');
module.exports = {
addons: [
'#storybook/addon-controls',
'#storybook/addon-docs',
'#storybook/addon-actions',
'#storybook/preset-scss',
],
webpackFinal: async (config, { configType }) => {
config.module.rules.push({
test: /\.scss$/,
use: ['style-loader', 'css-loader', 'sass-loader'],
include: path.resolve(__dirname, '../'),
});
config.module.rules.push({
resolve: {
alias: {
'#': path.resolve(__dirname, '../src'),
vue: 'vue/dist/vue.js',
'vue$': 'vue/dist/vue.esm.js',
},
},
});
// Return the altered config
return config;
},
};
preview.js
import { configure, addDecorator } from "#storybook/vue";
import "!style-loader!css-loader!sass-loader!./scss-loader.scss";
import "vuetify/dist/vuetify.css";
import "#mdi/font/css/materialdesignicons.css";
import i18n from '../src/plugins/i18n';
import Vue from "vue";
import Vuetify from "vuetify";
Vue.use(Vuetify);
Vue.prototype.$t = function(...args){
return i18n.t(...args);
}
addDecorator(() => ({
i18n,
vuetify: new Vuetify({
rtl: true,
icons: {
iconfont: "mdi",
}
}),
template:
'<v-app style="background-color: white"><v-main><story/></v-main></v-app>',
}));
// automatically import all files ending in *.stories.js
configure(require.context("../stories", true, /\.stories\.js$/), module);
manager.js
import "#storybook/addon-actions/register";
import "#storybook/addon-links/register";
Here is an example of my stories
import Calendar from "../src/components/Calendar/calendar.vue";
export default {
title: "Components/Calendar",
component: Calendar,
};
const Template = (args, { argTypes }) => ({
components: { Calendar },
props: Object.keys(argTypes),
template: `<Calendar v-bind="$props" />`,
});
export const WithLabel = Template.bind({});
WithLabel.args = {
label: "Pick a date",
};
I don't know What is the issue.
Thanks in advance.

Related

[Vue warn]: Error in data(): "TypeError: Cannot destructure property 'store' of '(0 , _compositionApi.useContext)(...)' as it is undefined."

I am setting up tests on a Nuxt2 app using #nuxtjs/composition-api and
#vue/test-utils.
In addition, there is a vue-CLI UI library using #vue/composition-api and vue-demi.
The issue is that even if the context is mocked as per solution here, our store is still undefined and the tests are failling.
///// SET UP /////
The Nuxt2 app is set as follow:
Package.json
"dependencies": {
"#nuxtjs/composition-api": "^0.20.2",
"core-js": "2",
}
"devDependencies": {
"#vue/test-utils": "^1.3.0",
"babel-core": "^7.0.0-bridge.0",
"babel-jest": "^28.1.0",
"jest": "^28.1.0",
"jest-environment-jsdom": "^28.1.0",
"jest-junit": "^14.0.0",
"vue-jest": "^3.0.7",
}
nuxt.config.js
alias: {
'vue-demi': '#nuxtjs/composition-api',
},
jest.config.js
module.exports = {
verbose: true,
testEnvironment: 'jsdom',
moduleFileExtensions: ['js', 'vue', 'json'],
moduleNameMapper: {
'^#/(.*)$': '<rootDir>/$1',
'^~/(.*)$': '<rootDir>/$1',
'^vue$': 'vue/dist/vue.common.js',
'^.+\\.(s?css|less)$': '<rootDir>/css_stub.js',
'^#atoms/(.*)$':
'<rootDir>/node_modules/UILIBRARY-path/$1',
'^#molecules/(.*)$':
'<rootDir>/node_modules/UILIBRARY-path/$1',
'^#organisms/(.*)$':
'<rootDir>/node_modules/UILIBRARY-path/$1',
},
transform: {
'^.+\\.js$': '<rootDir>/node_modules/babel-jest',
'.*\\.(vue)$': '<rootDir>/node_modules/vue-jest',
},
transformIgnorePatterns: ['<rootDir>/node_modules/UILIBRARY'],
moduleDirectories: ['<rootDir>/node_modules/'],
globals: {
'vue-jest': {
babelConfig: {
configFile: './babel.config.js',
},
},
},
setupFilesAfterEnv: ['<rootDir>/test/unit/vueSetup.js'],
collectCoverage: true,
collectCoverageFrom: ['components/**.vue', 'pages/**.vue'],
coverageDirectory: 'tmp/coverage',
reporters: ['default', ['jest-junit', { outputDirectory: 'tmp/coverage' }]],
coverageReporters: ['text', 'html', 'cobertura'],
coverageThreshold: {
global: {
statements: 0,
branches: 0,
functions: 0,
lines: 0,
},
},
}
test/vueSetp.js
import Vue from 'vue'
import Buefy from 'buefy'
import VueFormulate from 'UILIBRARY/vue-formulate/src/Formulate'
import { config } from '#vue/test-utils'
Vue.use(Buefy)
Vue.use(VueFormulate)
config.mocks.$t = (key) => key
config.mocks.$tc = (key) => key
///// Component and Test ////
Component.vue
export default {
name: 'Component',
setup() {
const {
store,
} = useContext()
// when consol.log(store), it is defined
const gettingStoreData = computed(
() =>
store.getters['data']?.key === 1
)
test.spec.js
import { mount } from '#vue/test-utils'
import Component from '#/path-to-component/Component.vue'
jest.mock('#nuxtjs/composition-api')
describe('Component.vue', () => {
const factory = (mockDataForm) => {
const wrapper = mount(Component, {
propsData: {
various data
},
mocks: {
$nuxt: {
context: {
store: {},
},
},
},
provide: {
form: mockDataForm
},
})
return wrapper
}
test('component mounts properly', () => {
const wrapper = factory({
various data
})
expect(wrapper.vm).toBeTruthy()
})
There were a couple of attempts to define/ mock the store globally:
vueSetup.js
setting a mock store globaly
config.mocks.store
adding $nuxt to Vue.prototype
Vue.prototype.$nuxt = {
context: {
store: {},
},
}
creating a new Vue and adding store to its prototype
const newVue = new Vue({})
newVue.prototype = (key) => key
{
context: {
store: { $auth: {} },
But all of it result in the same error as in the title:
TypeError: Cannot destructure property 'store' of '(0 , _compositionApi.useContext)(...)' as it is undefined.
So what else could be done in order to not have an undefined store and be able to run the tests properly?
Should Vuex be added (which sounds not good as it is a nuxt app)
Is it actually only related to the store and not the context since mocking $t and $tc works fine?
Could it be linked to the component code itself and not the test?
Many thanks in advance for your support and please let me know if there is any missing information or if you need more details.
Cheers,

Vue.js 3: Vue is not defined with mount when running unit tests with external library

I am currently experienced the following error when running my unit tests:
ReferenceError: Vue is not defined
> 10 | import hljsVuePlugin from '#highlightjs/vue-plugin';
| ^
Here is my component code:
<template>
<div>
<highlightjs autodetect :code="'hello world'" />
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import hljsVuePlugin from '#highlightjs/vue-plugin';
export default defineComponent({
components: {
highlightjs: hljsVuePlugin.component,
},
});
</script>
in my test file, I'm simply trying to mount this component:
const wrapper = mount(FooBlah, {
global: {
stubs: {
highlightjs: {
template: '<div />',
},
},
},
});
Here are my libraries versions:
"vue": "^3.2.33",
"#highlightjs/vue-plugin": "^2.1.2",
"highlight.js": "^11.6.0",
"#vue/cli-plugin-unit-jest": "~5.0.4",
"#vue/test-utils": "^2.0.2",
"#vue/vue3-jest": "^27.0.0",
Is there any way I could tell him to simply ignore the highlightjs the component in the jest.config ?
As mentionned in the comments, this fixed my issue:
jest.mock('#highlightjs/vue-plugin', () => ({
hljsVuePlugin: {
component: jest.fn(),
},
}));
const wrapper = mount(FooBlah, {
global: {
stubs: {
highlightjs: {
template: '<div />',
},
},
},
})
You can find more information here: How can I mock an ES6 module import using Jest?

TypeError: Cannot read properties of undefined (reading 'html')

I am trying to introduce Jest to my current project.
However, during the initial setup, I encountered this error and it is not running properly.
How can I solve this?
I am currently using vue2 from vue-cli.
● Test suite failed to run
TypeError: Cannot read properties of undefined (reading 'html')
at new JSDOMEnvironment (node_modules/jest-environment-jsdom/build/index.js:72:44)
at async TestScheduler.scheduleTests (node_modules/#jest/core/build/TestScheduler.js:317:13)
at async runJest (node_modules/#jest/core/build/runJest.js:407:19)
at async _run10000 (node_modules/#jest/core/build/cli/index.js:338:7)
at async runCLI (node_modules/#jest/core/build/cli/index.js:190:3)
This is my test code.
import SettlementProcessing from "#/views/calculate/SettlementProcessing.vue";
import { shallowMount } from "#vue/test-utils";
import Vuetify from "vuetify";
describe("Settlement Component", () => {
let vuetify;
beforeEach(() => {
vuetify = new Vuetify();
});
it("정산 처리 타이틀이 나와야 한다.", () => {
const sc = shallowMount(SettlementProcessing, { vuetify });
expect(true).toBe(true);
});
});
Here is my package.json.
"devDependencies": {
"#vue/cli-plugin-babel": "~4.5.0",
"#vue/test-utils": "^2.0.0-rc.21",
"babel-core": "^7.0.0-bridge.0",
"babel-jest": "^28.1.0",
"jest": "^28.1.0",
"vue-cli-plugin-vuetify": "~2.4.0",
"vue-jest": "^3.0.7",
}
Here is my jest.config.json.
// jest.config.js
module.exports = {
preset: "#vue/cli-plugin-unit-jest",
moduleFileExtensions: [
"js",
"json",
"vue",
],
transform: {
"^[^.]+.vue$": "vue-jest",
"^.+\\.js$": "babel-jest",
},
moduleNameMapper: {
"^#/(.*)$": "<rootDir>/src/$1",
},
testMatch: [
"**/__tests__/**/*.[jt]s?(x)",
"**/?(*.)+(spec|test).[jt]s?(x)",
],
testPathIgnorePatterns: ["/node_modules/", "/dist/"],
collectCoverage: false,
collectCoverageFrom: ["**/*.{js,vue}", "!**/node_modules/**"],
};
How can I solve this??
I had the same issue when updating my react app to jest 28. The issue was the missing jest-environment-jsdom package which was not yet necessary in jest 27.
See https://jestjs.io/docs/28.x/upgrading-to-jest28
You should create a localVue instance and use Vuetify on it. This can be achieved either in a tests/setup.js file (which runs for all jest tests) or separately in each unit test that uses Vuetify.
Sample code without setup.js (if you use setup.js, the code will be slightly different, you can check the documentation below)
import SettlementProcessing from "#/views/calculate/SettlementProcessing.vue";
import { createLocalVue, shallowMount } from "#vue/test-utils";
import Vuetify from "vuetify";
const localVue = createLocalVue()
localVue.use(Vuetify)
describe("Settlement Component", () => {
it("정산 처리 타이틀이 나와야 한다.", () => {
const sc = shallowMount(SettlementProcessing, { localVue } );
expect(true).toBe(true);
});
});
The documentation is here:
https://vuetifyjs.com/en/getting-started/unit-testing/#bootstrapping-vuetify
It was fixed for me by adding jest-environment-jsdom.

Inertia.js ssr: ReferenceError: document is not defined

I am using Laravel 8 with Inertia.js version 0.5.4. Can anyone help solve this problem? The app works fine without the ssr. I think the problem is in the Webpack config file.
ReferenceError: document is not defined
package.json
"#inertiajs/inertia": "^0.11.0",
"#inertiajs/inertia-vue": "^0.8.0",
"laravel-mix": "^6.0",
"sass": "~1.32",
"sass-loader": "^12.2.0",
"vue-cli-plugin-vuetify": "~2.3.1",
"vue-loader": "^15.9.6",
"vue-template-compiler": "^2.6.10",
"vuetify-loader": "^1.7.0",
"webpack": "^5.59.1",
"vue": "^2.6.14",
"vue-server-renderer": "^2.6.14",
"vuetify": "^2.5.5",
"vuetifyjs-mix-extension": "0.0.20",
"vuex": "^3.4.0",
"webpack-node-externals": "^3.0.0"
webpack.ssr.mix.js
const path = require('path')
const mix = require('laravel-mix')
const webpackNodeExternals = require('webpack-node-externals');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
mix
.options({ manifest: false, processCssUrls: false })
.js('resources/js/ssr.js', 'public/js')
.vue({
version: 2, options: { optimizeSSR: true }
})
.webpackConfig({
resolve: {
alias: {
'#resources': path.resolve('resources'),
'{Template}': path.resolve('resources/js/Themes/default'),
'#themeConfig': path.resolve('themeConfig.js'),
'#core': path.resolve('resources/#core'),
'#axios': path.resolve('resources/js/plugins/axios.js'),
'#user-variables': path.resolve('resources/sass/variables.scss'),
'#sass': path.resolve('resources/sass/'),
'apexcharts': path.resolve(__dirname, 'node_modules/apexcharts-clevision'),
'#': path.resolve('resources/js'),
},
},
plugins: [new MiniCssExtractPlugin(
{
filename: "[name].css",
chunkFilename: "[id].css",
linkType: false,
}
)],
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
options: {
// enable CSS extraction
extractCSS: true
}
}
// ...
]
},
target: 'node',
devtool: 'source-map',
externals: [webpackNodeExternals({
// this WILL include `jquery` and `webpack/hot/dev-server` in the bundle, as well as `lodash/*`
allowlist: [/^vuetify/, /^lodash/, 'vue-tel-input-vuetify/lib', /^vue-tel-input-vuetify^/, /^apexcharts^/, /^vue-apexcharts/, /apexcharts/, /apexcharts-clevision/, /^apexcharts-clevision/, /^apexcharts-clevision^/, /^vue-country-flag/, /\.css$/]
})],
})
ssr.js
import Vue from 'vue'
import {createRenderer} from 'vue-server-renderer'
import {createInertiaApp} from '#inertiajs/inertia-vue'
import createServer from '#inertiajs/server'
import PortalVue from 'portal-vue';
import store from './store'
import './plugins/acl'
import VueCompositionAPI from '#vue/composition-api'
import VueMeta from 'vue-meta'
import vuetify from '#/plugins/vuetify'
import Layout from '#/Layouts/AdminLayout.vue'
import AppLayout from "#/Layouts/AppLayout.vue"
import UserLayout from "#/Layouts/UserLayout.vue"
import ClientOnly from 'vue-client-only'
const moment = require('moment');
createServer((page) => createInertiaApp({
page,
render: createRenderer().renderToString,
// resolve: name => require(`./Pages/${name}`),
resolve: (name) => {
// const page = (await import(`./Pages/${name}`)).default
const page = require(`./Pages/${name}`);
if (page.layout === undefined && name.startsWith('Admin/')) {
page.layout = Layout
}
if (page.layout === undefined && name.startsWith('Dashboard/')) {
page.layout = Layout
}
if (page.layout === undefined && name.startsWith('UserDashboard/')) {
page.layout = UserLayout
}
if (page.layout === undefined && !name.startsWith('Admin/') && !name.startsWith('Dashboard/')) {
page.layout = AppLayout
}
return page
},
setup({app, props, plugin}) {
Vue.use(plugin);
Vue.use(VueCompositionAPI);
Vue.use(PortalVue);
Vue.component('client-only', ClientOnly)
Vue.use(VueMeta, {
// optional pluginOptions
refreshOnceOnNavigation: true
})
Vue.mixin({methods: {route}});
return new Vue({
vuetify: vuetify,
store,
render: h => h(app, props),
})
},
}))
npx mix --mix-config=webpack.ssr.mix.js
result : webpack compiled successfully
node public/js/ssr.js
error :
var style = document.createElement('style');
^
ReferenceError: document is not defined
at insertStyleElement (C:\laragon\www\test2\public\js\ssr.js:44989:15)
at addStyle (C:\laragon\www\test2\public\js\ssr.js:45104:13)
at modulesToDom (C:\laragon\www\test2\public\js\ssr.js:44977:18)
at module.exports (C:\laragon\www\test2\public\js\ssr.js:45135:25)
at Module../resources/sass/overrides.scss (C:\laragon\www\gooreo2\public\js\ssr.js:41942:145)
at webpack_require (C:\laragon\www\test2\public\js\ssr.js:130966:42)
at Module../resources/js/plugins/vuetify/default-preset/preset/index.js
(C:\laragon\www\test2\public\js\ssr.js:38073:1)
at webpack_require (C:\laragon\www\test2\public\js\ssr.js:130966:42)
at Module../resources/js/plugins/vuetify/index.js (C:\laragon\www\test2\public\js\ssr.js:38099:80)
at webpack_require
(C:\laragon\www\test2\public\js\ssr.js:130966:42)
public/js/ssr.js
You can fix it is by adding one line to your webpack.mix.js to opt in to using the vue-style-loader
mix
.js('resources/js/ssr.js', 'public/js')
.vue({
version: 3,
useVueStyleLoader: true // This should fix it!
});
Ref: https://aaronfrancis.com/2021/vue-referenceerror-document-is-not-defined-at-insertstyleelement

Import vue package in laravel

What is the corect way to import vue packages in laravel 5.6? It comes with vue and bootstrap preinstall. I see they are all compile in app.js from public directory but I can figure out how to import https://github.com/moreta/vue-search-select and use it. After I tried to import it on my own:
Error:
ncaught TypeError: Vue.component is not a function
At line:
Vue.component('search-user', __webpack_require__(42));
Until now I tried this:
assets/js/bootstrap.js:
import { BasicSelect } from 'vue-search-select';
window.BasicSelect = BasicSelect;
assets/js/app.js:
require('./bootstrap');
window.Vue = require('vue');
window.Vue = require('vue-search-select');
Vue.component('search-user', require('./components/SearchUser.vue'));
var app = new Vue({
el: '#app'
})
components
<template>
<basic-select :options="options"
:selected-option="item"
placeholder="select item"
#select="onSelect">
</basic-select>
</template>
<script>
export default {
data() {
return {
keywords: null,
options: []
};
},
watch: {
keywords(after, before) {
if (this.keywords.length > 0)
this.fetch();
}
},
methods: {
fetch() {
axios.get('/api/search', {params: {keywords: this.keywords}})
.then(response => this.options = response.data)
.catch(error => {
});
},
onSelect (item) {
this.item = item
},
reset () {
this.item = {}
},
selectOption () {
// select option from parent component
this.item = this.options[0]
},
components: {
BasicSelect
}
}
}
</script>
I ran: npm install and npm run watch:
"devDependencies": {
"ajv": "^6.0.0",
"bootstrap": "^4.0.0",
"cross-env": "^5.1",
"laravel-mix": "^2.0",
"lodash": "^4.17.4",
"popper.js": "^1.12",
"uikit": "^3.0.0-beta.35",
"vue": "^2.5.7",
"vue-search-select": "^2.5.0"
},
"dependencies": {
"axios": "^0.17.1",
"jquery": "^3.3.1"
}
I think that the simple will do
window.Vue = require('vue');
require('vue-search-select');
Then in your components you can import what you need on top:
import { BasicSelect } from 'vue-search-select';
export default {
data() {
return {
keywords: null,
options: [],
item: null
};
},
...
One missing detail that tricked me with this one, you need to register the components like this, otherwise it won't be found:
components: {
ModelSelect,
BasicSelect
},