InertiaJS and nWidart/laravel-modules loading Node package Problem - vue.js

I'm trying to build a moduler application with laravel-modules. I'm using Vue2 and InertiaJS on the front end and nWidart/laravel-modules on the backend.
In one of my modules I want to inlcude the vue-flowy package that no other module uses, and this module may not be installed on all installations of my app, so I don't want to load it in the root package.json file.
If I install the module in the primary package.json file, it works beautifully. But trying to install it in the module's package.json file will build in mix, but running the page gives me erors:
Modules_MyModule_Resources_js_Pages_Index_vue.js:660 [Vue warn]: $attrs is readonly.
Modules_MyModule_Resources_js_Pages_Index_vue.js:660 [Vue warn]: $listeners is readonly.
I'm assuming that the vue-flowy package is trying to build a secon Vue instance to run on, but I don't understand why or how to fix it.
/Modules/<myModule>/package.json
{
"private": true,
"scripts": {
"dev": "npm run development",
"development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
"watch": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --watch --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
"watch-poll": "npm run watch -- --watch-poll",
"hot": "cross-env NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js",
"prod": "npm run production",
"production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js"
},
"dependencies": {
"cross-env": "^7.0",
"laravel-mix": "^6.0.43",
"laravel-mix-merge-manifest": "^0.1.2",
"vue-flowy": "^0.3.1"
}
}
Vue Component Script section - this is pulled directly from the example on the vue-flowy website
<script>
import { VueFlowy, FlowChart } from 'vue-flowy';
export default {
components: { VueFlowy },
props: {
//
},
data() {
return {
chart: new FlowChart(),
}
},
mounted() {
const idea = this.chart.addElement('idea');
const A = this.chart.addElement("A", { label: "vscode" });
const B = this.chart.addElement("B", { label: "github" });
const C = this.chart.addElement("C", { label: "npm" });
idea.leadsTo(A).leadsTo(B);
A.leadsTo(C);
A.on("click", function() {
console.log("click!");
});
},
}
</script>
Main app.js file in /resources/js/app.js
import Vue from 'vue';
import { Link } from '#inertiajs/inertia-vue';
import BootstrapVue from 'bootstrap-vue';
////////////// Bunch of other stuff ///////////////
const el = document.getElementById('app');
InertiaProgress.init();
Vue.prototype.eventHub = new Vue();
new Vue({
render: h => h(App, {
props: {
initialPage: JSON.parse(el.dataset.page),
resolveComponent: (name) => {
// Determine if this is a primary page, or module page
let parts = name.split('::');
let module = false;
let page = false;
if(parts.length > 1)
{
// If it is a module page, load the module from the correct folder
module = parts[0];
page = parts[1];
if(module)
{
return import(`../../Modules/${module}/Resources/js/Pages/${page}.vue`)
.then(({ default: page }) =>
{
if (page.layout === undefined)
{
page.layout = Guest;
}
return page;
});
}
}
// primary page, load from the Pages folder
return import(`./Pages/${name}`)
.then(({ default: page }) =>
{
if (page.layout === undefined)
{
page.layout = Guest;
}
return page;
});
}
},
}),
}).$mount(el)

Related

"vue running in developer mode" warning appears even when running in production mode

I am running vue in production mode but i still get the warning of vue running in developer mode
the project is running fine and i can see that it running in production mode. but i am still getting the warning "You are running Vue in development mode.
Make sure to turn on production mode when deploying for production."
Below are my vue.config.js main.js and dockerfile
vue.config.js file
const webpack = require('webpack');
module.exports = {
devServer: {
host: "localhost",
port: 3123,
disableHostCheck: true,
},
publicPath: './',
configureWebpack: {
plugins: [
new webpack.DefinePlugin({
// allow access to process.env from within the vue app
'process.env': {
NODE_ENV: JSON.stringify(process.env.NODE_ENV)
}
}),
new webpack.ProvidePlugin({
jQuery: 'jquery',
'$': 'jquery',
}),
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
]
},
chainWebpack: config => {
if (process.env.NODE_ENV === "production") {
config.plugin('define')
.tap(definitions => {
definitions[0] = Object.assign(definitions[0], {
'process.env': {
NODE_ENV: "'production-with-warns'"
}
});
return definitions
});
}
config.module
.rule('vue')
.use('vue-loader')
.tap(args => {
args.compilerOptions.whitespace = 'preserve'
})
},
lintOnSave: false,
transpileDependencies: ['vue2-datatable-component']
};
main.js file
import Vue from 'vue';
import Vuex from 'vuex';
import VueRouter from 'vue-router';
import 'bootstrap'
import 'bootstrap/dist/css/bootstrap.css'
import 'bootstrap3-dialog';
import 'bootstrap3-dialog/dist/css/bootstrap-dialog.css'
import 'bootstrap-select';
import 'bootstrap-select/dist/css/bootstrap-select.css'
import 'cropperjs'
import 'cropperjs/dist/cropper.css'
import 'flag-icon-css/css/flag-icon.css'
import 'eonasdan-bootstrap-datetimepicker'
import 'eonasdan-bootstrap-datetimepicker/build/css/bootstrap-datetimepicker.css'
import 'font-awesome/css/font-awesome.css'
import './useCompositionApi'
import App from './atar/App.vue';
import store from './store.js';
import routerConfig from './router.js';
import filters from './filters.js';
import resource from './resource.js';
import directives from './directives';
import mixins from './mixins';
import i18n from './i18n';
import * as uiv from 'uiv';
import Datatable from 'vue2-datatable-component';
import VueScrollTo from 'vue-scrollto';
import VueNotification from 'vue-notification';
import AsyncComputed from 'vue-async-computed';
import extensions from './extensions';
import windowConfiguration from './windowConfiguration.js';
import VueAutosize from 'vue-autosize';
import errorHandler from "./errorHandler.js";
import EventBusPlugin from "./plugin/EventBusPlugin";
import WindowResizePlugin from "./plugin/WindowResizePlugin";
import ScrollPlugin from "./plugin/ScrollPlugin";
import 'nodelist-foreach';
import WSSubscriber from "./plugin/WSSubscriber";
import validationConfiguration from "./veeValidateConfiguration";
import { AppInitializer } from "./AppInitializer";
Vue.use(Vuex);
const originalPush = VueRouter.prototype.push;
VueRouter.prototype.push = function push(location) {
return originalPush.call(this, location).catch(error => {
if (error.name !== "NavigationDuplicated") {
throw error;
}
});
}
Vue.use(VueRouter);
Vue.use(uiv);
Vue.use(VueAutosize);
Vue.use(Datatable);
Vue.use(VueScrollTo, {
duration: 500,
easing: "ease",
offset: 0,
cancelable: true,
onDone: false,
onCancel: false,
x: false,
y: true
});
Vue.use(VueNotification);
Vue.use(AsyncComputed);
Vue.use(EventBusPlugin);
Vue.use(WindowResizePlugin);
Vue.use(ScrollPlugin);
Vue.use(WSSubscriber);
new AppInitializer()
.addStep(() => resource.configure())
.addStep((ctx) => store.configure(ctx))
.addStep(() => filters.configure())
.addStep(() => mixins.configure())
.addStep(() => directives.configure())
.addStep(() => i18n.configure())
.addStep(() => windowConfiguration.configure())
.addStep(() => validationConfiguration.configure())
.addStep(() => {
Vue.config.productionTip = false;
if (process.env.NODE_ENV !== 'development') {
Vue.config.devtools = false;
}
})
.addStep((ctx) => {
ctx['router'] = new VueRouter(routerConfig);
})
.addStep((ctx) => {
new Vue({
el: '#app',
router: ctx['router'],
store: new Vuex.Store(ctx['store']),
render: h => h(App)
});
})
.addStep((ctx) => {
ctx['router'].afterEach((route) => {
document.title = route.meta.titleProducer(route);
});
})
.addStep(() => extensions.configureGlobal())
.addStep(() => errorHandler.configure())
.initialize();
docker file
# a.k.a. node:lts-alpine
FROM node:14.17.4-alpine as app-build-stage
RUN apk add git
WORKDIR /app
RUN npm i -g #vue/cli-service#4.5.15 #vue/cli-plugin-babel#4.5.15 #vue/cli-plugin-eslint#4.5.15 #vue/cli-plugin-typescript#4.5.15 #vue/cli-plugin-unit-jest#4.5.15 vue-template-compiler#2.6.14 typescript#4.6.3 autoprefixer#8.6.5 less-loader#4.1.0 less#3.9.0 webpack#4.46.0 babel-loader#8.2.3 #babel/core#7.16.0
COPY ./package*.json ./legal_notice.sh ./
RUN npm ci --production
RUN npm link #vue/cli-service #vue/cli-plugin-babel #vue/cli-plugin-eslint #vue/cli-plugin-typescript #vue/cli-plugin-unit-jest vue-template-compiler typescript autoprefixer less-loader less webpack babel-loader #babel/core
COPY . .
ENV NODE_ENV production
RUN export freeMem=$(free -m | grep Mem | awk '{print $4}') &&\
export minRequiredMem=2048 &&\
if [[ $freeMem -lt $minRequiredMem ]];\
then\
echo "[WARNING] Free memory ($freeMem MB) is less than the required limit ($minRequiredMem MB)." &&\
echo "[WARNING] Build is likely to fail due to insufficient memory.";\
fi;\
npm run build;\
if [[ ! -d "/app/dist" ]];\
then echo "The npm build process has failed and /app/dist folder is not found.";\
if [[ $freeMem -lt $minRequiredMem ]];\
then\
echo "Free memory : $freeMem MB";\
echo "Minimum memory required : $minRequiredMem MB";\
echo "The build has failed most likely due to insufficient memory.";\
exit 1;\
fi\
fi
WORKDIR /app
COPY ./docker/package*.json ./docker/server.js ./docker/apiinfo.js.template ./docker/entrypoint.sh /app/
RUN apk upgrade
RUN npm ci
# there are some overlay issues in the Jenkins so rm -rf ..../npm/ is not applicable
RUN find /usr/local/lib/node_modules/npm/ -type f -exec rm -f {} \;
COPY --from=app-build-stage /app/dist /app/static/
ENTRYPOINT ["./entrypoint.sh"]
Check your build command under package.json file, the development mode is set in the build command. like so...
"scripts": {
"serve": "vue-cli-service serve",
"devserve": "run-s build:dev watch",
"lint": "vue-cli-service lint",
"build": "vue-cli-service build --mode development",
}
I usually have two build commands configured, one to build for production and one for development
"scripts": {
"serve": "vue-cli-service serve",
"devserve": "run-s build:dev watch",
"lint": "vue-cli-service lint",
"build:dev": "vue-cli-service build --mode development",
"build:prod": "vue-cli-service build --mode production",
}
Make sure to run npm run build:prod for a production build and then deploy and the error should not appear.

Vue test utils for Vue3 : document is not defined

I try to bootstrap a simple app based on the following Vue3, Vite, Vitest
I also installed the vue 3 compatible version of vue test utils to test vue components.
I have an error trying to replicate the basic example in the docs :
import { mount } from "#vue/test-utils";
import { expect, test } from 'vitest'
// The component to test
const MessageComponent = {
template: "<p>{{ msg }}</p>",
props: ["msg"],
};
test("displays message", () => {
const wrapper = mount(MessageComponent, {
props: {
msg: "Hello world",
},
});
// Assert the rendered text of the component
expect(wrapper.text()).toContain("Hello world");
});
FAIL src/tests/hello-world.test.ts > displays message
ReferenceError: document is not defined
❯ Proxy.mount node_modules/#vue/test-utils/dist/vue-test-utils.cjs.js:7840:14
7838| addToDoNotStubComponents(component);
7839| registerStub({ source: originalComponent, stub: component });
7840| var el = document.createElement('div');
| ^
7841| if (options === null || options === void 0 ? void 0 : options.attachTo) {
7842| var to = void 0;
Re-running tests... [ src/tests/hello-world.test.ts ]
My package.json
{
"name": "vite-vue3-poc",
"version": "0.0.0",
"scripts": {
"serve": "vite preview",
"build": "vite build",
"coverage": "vitest --coverage",
"dev": "vite",
"preview": "vite preview",
"test": "vitest"
},
"dependencies": {
"#mdi/font": "5.9.55",
"prettier": "^2.5.1",
"roboto-fontface": "*",
"vue": "^3.2.25",
"vuetify": "^3.0.0-alpha.0",
"webfontloader": "^1.0.0"
},
"devDependencies": {
"#vitejs/plugin-vue": "^2.0.0",
"#vue/cli-plugin-babel": "5.0.0-beta.7",
"#vue/cli-service": "5.0.0-beta.7",
"#vue/test-utils": "^2.0.0-rc.18",
"#vuetify/vite-plugin": "^1.0.0-alpha.3",
"sass": "^1.38.0",
"sass-loader": "^10.0.0",
"vite": "^2.7.2",
"vitest": "^0.1.23",
"vue-cli-plugin-vuetify": "~2.4.5",
"vuetify-loader": "^2.0.0-alpha.0"
}
}
vite.config.js
import { defineConfig } from "vite";
import vue from "#vitejs/plugin-vue";
import vuetify from "#vuetify/vite-plugin";
import path from "path";
/// <reference types="vitest" />
// Configure Vitest (https://vitest.dev/config)
// https://vitejs.dev/config/
export default defineConfig({
test: {
/* for example, use global to avoid globals imports (describe, test, expect): */
// globals: true,
},
plugins: [
vue(),
// https://github.com/vuetifyjs/vuetify-loader/tree/next/packages/vite-plugin
vuetify({
autoImport: true,
}),
],
define: { "process.env": {} },
resolve: {
alias: {
"#": path.resolve(__dirname, "src"),
},
},
});
Finally fixed it by manually installing jsdom and declaring it in vite.config.ts
export default defineConfig({
test: {
globals: true,
environment: "jsdom",
},
...
}
Like others have pointed out, you need to set environment: 'jsdom' in vitest.config.ts. Alternatively, you could set environment: 'happy-dom'.
In the example provided by the Vitest documentation, they used to use happy-dom instead of jsdom. From what I gather, happy-dom is a faster alternative to jsdom. I'm using happy-dom in my project, and I'm happy with it! :)
EDIT: I changed my wording to reflect the fact that the Vitest example used to use happy-dom. As of this writing, it uses jsdom.
No need to install jsdom manually. By setting environment: "jsdom" in the test property, Vitest automatically asks you if you want to install it.
This config helped me
Your vite.config.ts
import { fileURLToPath, URL } from "node:url"
import { defineConfig } from "vite"
import type { UserConfig as VitestUserConfigInterface } from "vitest/config"
import vue from "#vitejs/plugin-vue"
const vitestConfig: VitestUserConfigInterface = {
test: {
globals: true,
environment: "jsdom",
},
}
export default defineConfig({
plugins: [vue()],
resolve: {
alias: {
"#": fileURLToPath(new URL("./src", import.meta.url)),
},
},
test: vitestConfig.test,
})

Mocking RNCamera with Detox does not work, non-mock impl is called

I'm trying to mock out RNCamera in a Detox test. However, the mock camera does not override the real implementation.
I followed these instructions and incorporated advice from these posts.
The result is:
The mock camera class file itself is executed (top-level logging is printed)
Mock camera's takePictureAsync and render functions are not called
Emulator's default camera is used
I have also tried:
RN_SRC_EXT=e2e react-native run-android, then detox test
adding "test-runner": "jest" and callingjest.mock` in the best body
replacing RN_SRC_EXT=e2e with RN_SRC_EXT=e2e.js
cleanup: rm -rf node_modules; yarn install etc.
My app layout is like (non-test-relevant content excluded):
package.json:
{
...
"dependencies": {
...
"#types/jest": "^24.0.21",
"react": "16.9.0",
"react-dom": "latest",
"react-native": "0.61.1",
"react-native-camera": "^3.6.0",
},
"devDependencies": {
"#babel/core": "latest",
"#babel/preset-env": "latest",
"#babel/register": "latest",
"#babel/preset-react": "latest",
"#react-native-community/eslint-config": "^0.0.5",
"babel-jest": "^24.9.0",
"detox": "^14.5.1",
"jest": "^24.9.0",
"jest-fetch-mock": "^2.1.2",
"metro-react-native-babel-preset": "^0.56.0",
"mocha": "^6.2.2",
"react-test-renderer": "16.9.0"
},
"detox": {
"configurations": {
"android.emu.debug": {
"binaryPath": "android/app/build/outputs/apk/debug/app-debug.apk",
"build": "RN_SRC_EXT=e2e cd android && ./gradlew assembleDebug assembleAndroidTest -DtestBuildType=debug && cd ..",
"type": "android.emulator",
"device": {
"avdName": "Pixel_2_API_29"
}
},
"android.emu.release": {
"binaryPath": "android/app/build/outputs/apk/release/app-release.apk",
"build": "RN_SRC_EXT=e2e cd android && ./gradlew assembleRelease assembleAndroidTest -DtestBuildType=release && cd ..",
"type": "android.emulator",
"device": {
"avdName": "Pixel_2_API_29"
}
}
}
}
}
e2e/config.js:
require('#babel/register')({
//cache: true,
presets: [require('metro-react-native-babel-preset')],
plugins: [require('#babel/plugin-transform-runtime').default],
only: ['./e2e', './js'],
ignore: ['node_modules']
});
e2e/config.json:
{
"setupFilesAfterEnv": ["./init.js"],
"testEnvironment": "node",
"reporters": ["detox/runners/jest/streamlineReporter"],
"testMatch": ["**/__tests__/**/*.js?(x)", "**/?(*.)(e2e).js?(x)"],
"verbose": true
}
e2e/mocha.opts:
--recursive
--timeout 300000
--bail
--file e2e/init.js
--require e2e/config.js
--require e2e/config.json
--file e2e/react-native-camera.e2e.js
metro.config.js:
const defaultSourceExts = require('metro-config/src/defaults/defaults').sourceExts
module.exports = {
transformer: {
getTransformOptions: async () => ({
transform: {
experimentalImportSupport: false,
inlineRequires: false,
},
}),
},
resolver: {
sourceExts: process.env.RN_SRC_EXT
? process.env.RN_SRC_EXT.split(',').concat(defaultSourceExts)
: defaultSourceExts
}
};
// printed as: module.exports.resolver from e2e { sourceExts: [ 'e2e', 'js', 'json', 'ts', 'tsx' ] }
console.log("module.exports from e2e", module.exports);
e2e/config.json:
{
"setupFilesAfterEnv": ["./init.js"],
"testEnvironment": "node",
"reporters": ["detox/runners/jest/streamlineReporter"],
// tried with and without this line
"testMatch": ["**/__tests__/**/*.js?(x)", "**/?(*.)(e2e).js?(x)"],
"verbose": true
}
test.spec.js
describe('Example', () => {
beforeEach(async () => {
await device.reloadReactNative();
});
it('should blah blah balh', async () => {
// test implementation
});
});
init.js:
const detox = require('detox');
const config = require('../package.json').detox;
const adapter = require('detox/runners/mocha/adapter');
before(async () => {
await detox.init(config);
});
beforeEach(async function() {
await adapter.beforeEach(this);
});
afterEach(async function() {
await adapter.afterEach(this);
});
after(async () => {
await detox.cleanup();
});
e2e/react-native-camera.e2e.js: (from here)
import React from 'react';
const timeout = ms => new Promise(resolve => setTimeout(resolve, ms));
// This IS printed on detox test -c android.emu.debug.
console.log("executing react-native-camera-e2e.js");
export class RNCamera extends React.Component {
static Constants = {
Aspect: {},
BarCodeType: {},
Type: { back: 'back', front: 'front' },
CaptureMode: {},
CaptureTarget: {},
CaptureQuality: {},
Orientation: {},
FlashMode: {},
TorchMode: {},
};
takePictureAsync = async () => {
console.log("mock RNCamera takePictureAsync"); // Never printed
await timeout(2000);
return { uri: './static-image.jpg' };
};
render() {
// This is never printed.
console.log("mock RNCamera render()");
return null;
}
}
export const takePictureAsync = async () => {
console.log("mock RNCamera takePictureAsync"); // never printed
await timeout(2000);
return { uri: './static-image.jpg' };
};
export default RNCamera;
Mocking with detox and mocking with Jest are 2 different things.
In order to be able to mock third party module with detox, you can create a proxy component provider that will decide what component to use. So that you will be able to load the mocked version for detox testing.
In your case you can create a CameraProvider.js that will just export the real RNCamera:
export { RNCamera } from 'react-native-camera';
Then you will need to create the mocked version CameraProvider.e2e.js alongside the previous file for end-to-end testing with detox:
import React from 'react';
const timeout = ms => new Promise(resolve => setTimeout(resolve, ms));
export class RNCamera extends React.Component {
static Constants = {
Aspect: {},
BarCodeType: {},
Type: { back: 'back', front: 'front' },
CaptureMode: {},
CaptureTarget: {},
CaptureQuality: {},
Orientation: {},
FlashMode: {},
TorchMode: {},
AutoFocus: { on: {} },
WhiteBalance: { auto: {} },
};
takePictureAsync = async () => {
console.log('mock RNCamera takePictureAsync');
await timeout(2000);
return { uri: './static-image.jpg' };
};
render() {
console.log('mock RNCamera render()');
return null;
}
}
Finally, if you have correctly configured your metro bundler in the root folder of your react-native project, you should be able to either build your app or start the bundler (for debug targets) with the RN_SRC_EXT=e2e.js env variable to let the metro bundler know which file extensions it should replace.
Gomino's answer is correct but its missing that you will need to import from the proxy component in the code under test.
E.G. Do this in your component:
import { RNCamera } from '../proxies/RNCamera';
And not
import { RNCamera } from 'react-native-camera';
Then you need to make sure the bundler is started with the RN_SRC_EXT=e2e.js env variable. So my detox test scripts (in package.json) look something like this:
"start-detox-metro": "RN_SRC_EXT=e2e.js react-native start",
"detox-build": "detox build --configuration android.emu.debug",
"detox test": "detox test --configuration android.emu.debug --reuse",
Run the scripts in that order.
The metro bundler will handle swapping the .e2e.js files in place of the original proxy file (but only when the env var "RN_SRC_EXT=e2e.js" is set).

Vue cli delete the outputDir folder during serve

I'm trying to use VueCLI and delete the output directory during serve, so that I can use this in my php files and determine whether to load dist assets or load via localhost:8080.
So in my vue.config.js I have:
module.exports = {
outputDir:'web',
configureWebpack: config => {
if (process.env.NODE_ENV !== 'production') {
console.log("its serve") // this logs
// here delete the outputDir
}
}
}
How do i delete a folder using the VueCLI since by default during serve my app is never deleted.
Use rimraf to remove directory.
const rimraf = require("rimraf");
module.exports = {
outputDir: 'web',
configureWebpack: config => {
if (process.env.NODE_ENV !== 'production') {
rimraf.sync(config.outputDir);
// rimraf.sync('web')
}
}
};
Alternative:
// package.json
"scripts": {
"serve": "rimraf web && vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
}

[Vue warn]: Unknown custom element: <nuxt-link> - When running jest unit tests

Problem
I'm using nuxt 1.4 with routing using Jest to do unit testing. My application doesn't throw errors and seems to work perfectly. However when running my unit test npm run unit (which runs jest) it throws an error in the terminal: [Vue warn]: Unknown custom element: <nuxt-link> - did you register the component correctly? For recursive components, make sure to provide the "name" option.
Expected
I would expect it to not throw this error since my application is working.
Files
package.json:
{
"name": "vue-starter",
"version": "1.0.0",
"description": "Nuxt.js project",
"private": true,
"scripts": {
"dev": "nuxt",
"build": "nuxt build",
"start": "nuxt start",
"generate": "nuxt generate",
"lint": "eslint --ext .js,.vue --ignore-path .gitignore .",
"precommit": "npm run lint",
"test": "npm run lint && npm run unit",
"unit": "jest",
"unit:report": "jest --coverage"
},
"dependencies": {
"babel-jest": "^22.4.1",
"jest-serializer-vue": "^1.0.0",
"node-sass": "^4.7.2",
"npm": "^5.7.1",
"nuxt": "^1.0.0",
"sass-loader": "^6.0.7",
"vue-jest": "^2.1.1"
},
"devDependencies": {
"#vue/test-utils": "^1.0.0-beta.12",
"babel-eslint": "^8.2.1",
"eslint": "^4.15.0",
"eslint-friendly-formatter": "^3.0.0",
"eslint-loader": "^1.7.1",
"eslint-plugin-vue": "^4.0.0",
"jest": "^22.4.2"
},
"browserslist": [
"> 1%",
"last 2 versions",
"not ie <= 8"
],
"jest": {
"moduleFileExtensions": [
"js",
"vue"
],
"transform": {
"^.+\\.js$": "<rootDir>/node_modules/babel-jest",
".*\\.(vue)$": "<rootDir>/node_modules/vue-jest"
},
"snapshotSerializers": [
"<rootDir>/node_modules/jest-serializer-vue"
]
}
}
The component that I test:
<template>
<div>
<nuxt-link class="name" :to="{ path: `entity/${item.id}`, params: { id: item.id }}">{{item.name}}</nuxt-link>
<button class="connect" #click="connect">{{ btnText }}</button>
</div>
</template>
<script>
// import nuxtLink from '../.nuxt/components/nuxt-link';
const connectionStatusMap = [
'Connect',
'Connected',
'Pending',
'Cancel',
];
export default {
/*components: {
'nuxt-link': nuxtLink,
},*/
props: {
item: {
type: Object
}
},
...
}
</script>
My test script:
import TestItem from '../components/TestItem';
import { shallow, mount, createLocalVue } from '#vue/test-utils';
import Vuex from 'vuex';
import VueRouter from 'vue-router';
const localVue = createLocalVue()
localVue.use(Vuex)
localVue.use(VueRouter)
...
it(`should show the entity`, () => {
const wrapper = mount(TestItem, {
propsData: { item },
localVue,
store,
// stubs: ['nuxt-link'],
})
expect(wrapper.find('.name').text()).toBe(item.name);
});
it(`should show allow me to connect if I'm not yet connected`, () => {
const wrapper = shallow(TestItem, {
propsData: { item },
localVue,
store,
stubs: ['nuxt-link'],
})
expect(wrapper.find('.connect').text()).toBe('Connect');
});
...
I tried
I tried creating a localVue and also stubbing the component as suggested in this github comment
I also tried shallow/mount but that did not seem to work either.
This is how I was able to get rid of the annoying warning:
Include RouterLinkStub, eg.:
import { shallowMount, createLocalVue, RouterLinkStub } from '#vue/test-utils';
Map NuxtLink stub to RouterLinkStub
const wrapper = shallowMount(TestItem, {
...
stubs: {
NuxtLink: RouterLinkStub
}
})
And in case you were checking nuxt-link text or something, change:
const link = wrapper.find('nuxt-link');
to
const link = wrapper.find(RouterLinkStub);
Found this gold on https://onigra.github.io/blog/2018/03/19/vue-test-utils-router-link-stub/
Good thing you don't need to know japanese to read code...
Although the answers provided could work. What I ended up using was based on this guide
const wrapper = mount(TestItem, {
propsData: { item },
localVue,
store,
stubs: {
NuxtLink: true,
// Any other component that you want stubbed
},
});
I managed to get it working using this workaround for Storybook:
import { mount, createLocalVue } from '#vue/test-utils'
import Component from '#/components/Component.vue'
const localVue = createLocalVue()
localVue.component('nuxt-link', {
props: ['to'],
template: '<slot>NuxtLink</slot>',
})
describe('Test Component', () => {
const wrapper = mount(Component, {
stubs: ['nuxt-link'],
localVue
})
})
I added below lines of code to get this working.
In your test file
import NuxtLink from "path to nuxt-link.js"
Mycomponent.components.NuxtLink = NuxtLink
In your jest conf file
transformIgnorePatterns: [
"path to nuxt-link.js"
],
Or you could add below line in mount options
mount(Mycomponent, {stubs: ["nuxt-link"]})
I have:
// path: ./test/jest.setup.js
import Vue from 'vue'
import VueTestUtils from '#vue/test-utils'
// Mock Nuxt components
VueTestUtils.config.stubs['nuxt-link'] = '<a><slot /></a>'
VueTestUtils.config.stubs['no-ssr'] = '<span><slot /></span>'
and
// path: ./jest.config.js
module.exports = {
// ... other stuff
setupFilesAfterEnv: ['./test/jest.setup.js']
}
... and this solves all my jest test in the nuxt app
To anyone getting the Unknow custom element: <router-link>
My issue was, I used mount instead of shallow when creating the component.
shallow usage:
Like mount, it creates a Wrapper that contains the mounted and
rendered Vue component, but with stubbed child components.
Source: https://vue-test-utils.vuejs.org/en/api/shallow.html
Here is a working example
import { shallow } from '#vue/test-utils';
import ContentCard from '../../components/ContentCard.vue';
import NuxtLink from '../../.nuxt/components/nuxt-link';
const createComponent = propsData => shallow(ContentCard, { propsData });
describe('ContentCard', () => {
let component;
beforeEach(() => {
ContentCard.components = ContentCard.components || {};
ContentCard.components.NuxtLink = NuxtLink;
});
describe('Properties', () => {
it('has an imgSrc property', () => {
component = createComponent({ imgSrc: 'X' });
expect(component.props().imgSrc).toBe('X');
});
});
});
...
import NuxtLink from '../.nuxt/components/nuxt-link.js'
...
TestItem.components = TestItem.components || {};
TestItem.components.NuxtLink = NuxtLink;
const wrapper = shallow(TestItem, {
...
});
...
// test/jestSetup.js
import Vue from 'vue'
import Vuetify from 'vuetify'
import { config } from '#vue/test-utils'
Vue.use(Vuetify)
config.stubs.NuxtLink = { template: '<a><slot /></a>' }