Jest Test failing for API endpoint with 404 error - vue.js

I am trying to test my serverMiddleware in nuxt which has API routes
It has a single route /api/v1/test which returns a json true
My api/index.js file
import express from 'express'
const app = express()
app.use(express.json())
app.use(express.urlencoded({ extended: true }))
app.get('/test', (req, res) => res.json(true))
export default {
path: '/api/v1',
handler: app,
}
Here is my api.spec.js file which contains the test returning 404
If I test my route / it returns a 200
My test/backend/api.spec.js file
import { resolve } from 'path'
import { Nuxt, Builder } from 'nuxt'
import supertest from 'supertest'
// We keep the nuxt and server instance
// So we can close them at the end of the test
let nuxt = null
// Init Nuxt.js and create a server listening on localhost:4000
beforeAll(async () => {
const config = {
dev: process.env.NODE_ENV !== 'production',
rootDir: resolve(__dirname, '../', '../'),
mode: 'universal',
}
nuxt = new Nuxt(config)
await new Builder(nuxt).build()
await nuxt.server.listen(3000, 'localhost')
}, 30000)
// Close server and ask nuxt to stop listening to file changes
afterAll(() => {
nuxt.close()
})
describe('GET /api/v1/test', () => {
test('returns status code 200', (done) => {
supertest(nuxt.server.app).get('/api/v1/test').expect(200, done)
})
})
My jest.config.js file
module.exports = {
moduleNameMapper: {
'^#/(.*)$': '<rootDir>/$1',
'^~/(.*)$': '<rootDir>/$1',
'^vue$': 'vue/dist/vue.common.js',
},
moduleFileExtensions: ['js', 'vue', 'json'],
transform: {
'^.+\\.js$': 'babel-jest',
'.*\\.(vue)$': 'vue-jest',
},
collectCoverage: true,
collectCoverageFrom: [
'<rootDir>/components/**/*.vue',
'<rootDir>/pages/**/*.vue',
],
}
Can someone kindly suggest why the test is failing

In my case, it was because of the jest configuration.
my jest.config.js
testEnvironment: 'jsdom'
api.test(spec).file requires node environment.
I didn't modify it. Instead, I modified the api.test.js file.
I just added the comment code below at the head of the file.
solved) my api.test.js
/**
* #jest-environment node
*/
link : https://jestjs.io/docs/configuration#testenvironment-string

Related

Built code errors in Electron: Failed to execute 'querySelector' on 'Document'

Using Vite to build the app, I am getting the following error inside Electron:
index.c160f204.js:9 DOMException: Failed to execute 'querySelector' on 'Document': 'link[href="/C:UsersrankDocumentsSchoolCheckInElectronReaderdist/assets/Home.b0f26e4d.js"]' is not a valid selector.
It appears to me that the path inside the built code has the slashes removed, but I have no idea on how to solve that since it's generated code.
Using Node 17.9.0 on Windows 11 10.0.22000 Build 22000
Electron main.js:
const { app, BrowserWindow } = require("electron");
const path = require("path");
function createWindow() {
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, "preload.js"),
},
});
win.loadFile("dist/index.html");
}
app.whenReady().then(() => {
createWindow();
app.on("activate", () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
});
app.on("window-all-closed", () => {
if (process.platform !== "darwin") {
app.quit();
}
});
Electron preload.js:
window.addEventListener("DOMContentLoaded", () => {
const replaceText = (selector, text) => {
const element = document.getElementById(selector);
if (element) element.innerText = text;
};
for (const type of ["chrome", "node", "electron"]) {
replaceText(`${type}-version`, process.versions[type]);
}
});
Vite.config.ts:
import { defineConfig } from 'vite'
import vue from '#vitejs/plugin-vue'
import * as path from "path";
// https://vitejs.dev/config/
export default defineConfig({
base: path.resolve(__dirname, './dist'),
plugins: [
vue(),
],
})
Using vue-tsc --noEmit && vite build to build and electron . to start.
If you copy/paste the faulty code into a browser console, you will notice a non UTF-8 character in your link, between Users and rank.
Get rid of it and it should work.
The simplest way to fix this would be to move the project to a path which doesn't contain weird chars (e.g: C:/projects/)

Vue Server Side Rendering: Error in beforeCreate hook: ReferenceError: document is not defined

It happens when add <style></style> in .vue file.
[Vue warn]: Error in beforeCreate hook: "ReferenceError: document is not defined"
I mostly wrote code based on tutorial site.
https://github.com/vuejs/vue-hackernews-2.0/
src/App.vue
<template>
<div class="red">Hello from App.vue</div>
</template>
<script>
export default { name: "App" }
</script>
<style lang="scss" scoped> <-- Without style works well...
.red { color: red; }
</style>
src/app.js
import Vue from 'vue'
import App from './App.vue'
export function createApp() {
let app = new Vue({
render: h => h(App)
})
}
return { app }
}
src/entry-server.js
import { createApp } from './app'
export default context => {
return new Promise((resolve, reject) => {
const { app } = createApp()
resolve(app)
})
}
src/entry-client.js
import { createApp } from './app'
const { app } = createApp()
app.$mount('#app')
webpack.config.js
const path = require('path')
const webpack = require('webpack')
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
const { VueLoaderPlugin } = require('vue-loader')
const env = process.env.NODE_ENV || 'development'
const isProd = env === 'production'
const baseConfig = {
mode: env,
devtool: isProd
? false
: 'source-map',
output: {
path: path.resolve(__dirname, 'dist'),
publicPath: '/dist/',
filename: '[name].js'
},
module: {
noParse: /es6-promise\.js$/,
rules: [
{
test: /\.css$/,
use: [
'vue-style-loader',
'style-loader',
'css-loader',
],
},
{
test: /\.scss$/,
use: [
'vue-style-loader',
'style-loader',
'css-loader',
'sass-loader'
],
},
{
test: /\.vue$/,
loader: 'vue-loader',
options: {
compilerOptions: {
preserveWhitespace: false
}
}
},
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/,
options: {
presets: ['#babel/preset-env'],
}
},
{
test: /\.(png|jpg|gif|svg|jpeg)$/,
loader: 'url-loader',
options: {
limit: 10000,
name: '[name].[ext]?[hash]'
}
},
],
},
performance: {
hints: false
},
plugins: isProd
? [
new VueLoaderPlugin()
]
: [
new VueLoaderPlugin(),
new FriendlyErrorsPlugin()
]
}
const VueSSRClientPlugin = require('vue-server-renderer/client-plugin')
const { merge } = require('webpack-merge')
const VueSSRClientConfig = merge(baseConfig, {
entry: {
app: './src/entry-client.js'
},
resolve: {
alias: {
'create-api': './create-api-client.js',
},
extensions: ['.js', '.vue']
},
plugins: [
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development'),
'process.env.VUE_ENV': '"client"'
}),
new VueSSRClientPlugin()
]
})
const nodeExternals = require("webpack-node-externals")
const VueSSRServerPlugin = require('vue-server-renderer/server-plugin')
const VueSSRServerConfig = merge(baseConfig, {
target: 'node',
entry: './src/entry-server.js',
output: {
filename: 'server-bundle.js',
path: path.resolve(__dirname, 'dist'),
libraryTarget: 'commonjs2'
},
resolve: {
alias: {
'create-api': './create-api-server.js',
},
extensions: ['.js', '.vue']
},
externals: nodeExternals({
allowlist: /[\.css|\.scss]$/
}),
plugins: [
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development'),
'process.env.VUE_ENV': '"server"'
}),
new VueSSRServerPlugin()
]
})
module.exports = [VueSSRServerConfig, VueSSRClientConfig]
setup-dev-server.js
const fs = require('fs')
const path = require('path')
const MFS = require('memory-fs')
const webpack = require('webpack')
const serverConfig = require('./webpack.config')[0]
const clientConfig = require('./webpack.config')[1]
const readFile = (fs, file) => {
try {
return fs.readFileSync(path.join(clientConfig.output.path, file), 'utf-8')
} catch (e) {}
}
module.exports = function setupDevServer(app, cb) {
let bundle
let clientManifest
let ready
const readyPromise = new Promise(r => { ready = r })
const update = () => {
if (bundle && clientManifest) {
ready()
cb(bundle, { clientManifest })
}
}
// modify client config to work with hot middleware
clientConfig.entry.app = ['webpack-hot-middleware/client', clientConfig.entry.app]
clientConfig.output.filename = '[name].js'
clientConfig.plugins.push(
new webpack.HotModuleReplacementPlugin(),
)
// dev middleware
const clientCompiler = webpack(clientConfig)
const devMiddleware = require('webpack-dev-middleware')(clientCompiler, {
publicPath: clientConfig.output.publicPath,
noInfo: true
})
app.use(devMiddleware)
clientCompiler.hooks.done.tap('done', stats => {
stats = stats.toJson()
stats.errors.forEach(err => console.error(err))
stats.warnings.forEach(err => console.warn(err))
if (stats.errors.length) return
clientManifest = JSON.parse(readFile(
devMiddleware.fileSystem,
'vue-ssr-client-manifest.json'
))
update()
})
// hot middleware
app.use(require('webpack-hot-middleware')(clientCompiler, { heartbeat: 1000 }))
// watch and update server renderer
const serverCompiler = webpack(serverConfig)
const mfs = new MFS()
serverCompiler.outputFileSystem = mfs
serverCompiler.watch({}, (err, stats) => {
if (err) throw err
stats = stats.toJson()
if (stats.errors.length) return
bundle = JSON.parse(readFile(mfs, 'vue-ssr-server-bundle.json'))
update()
})
return readyPromise
}
server.js
const path = require('path')
const express = require('express')
const app = express()
const resolve = file => path.resolve(__dirname, file)
const isProd = process.env.NODE_ENV === 'production'
const { createBundleRenderer } = require('vue-server-renderer')
function createRenderer(bundle, options) {
return createBundleRenderer(bundle, Object.assign(options, {
basedir: resolve('./dist'),
runInNewContext: !isProd,
}))
}
let renderer
let readyPromise
if (isProd) {
const bundle = require('./dist/vue-ssr-server-bundle.json')
const clientManifest = require('./dist/vue-ssr-client-manifest.json')
renderer = createRenderer(bundle, { clientManifest })
} else {
readyPromise = require('./setup-dev-server')(
app,
(bundle, options) => {
renderer = createRenderer(bundle, options)
}
)
}
function render(req, res) {
const context = req.body || {}
const { requestId } = req.body || {}
renderer.renderToString(context, (err, html) => {
// return json
res.json({ requestId, html })
})
}
app.get('/', isProd ? render : (req, res) => {
readyPromise.then(() => render(req, res))
})
app.listen(9991)
Pretty sure that this is to do with your webpack coniguration. I think it's because style loader is trying to inject your styles into the DOM (which obviously is not present on the server side). Hence the reference error.
I'm not 100% sure, but try only using vue-style-loader. There's no need to put it in a chain with style-loader as they are pretty much doing the same thing.
Also run your build command on the project and take a look into the server-bundle. That will show you who's trying to access the DOM.
EDIT:
As a general approach to what you're trying to do, you should also include sass/css in one single rule, like this:
{
test: /\.(sa|sc|c)ss$/,
use: ['vue-style-loader', 'css-loader', 'sass-loader']
},
if u have sass pack you can change only webpack.config.js:
module: {
loaders: [{
test: /\.js$/,
loader: 'babel',
exclude: /node_modules/
}, {
test: /\.vue$/,
loader: 'vue'
}, {
test: /\.s[a|c]ss$/,
loader: 'style!css!sass'
}]
},
vue: {
loaders: {
scss: 'style!css!sass'
}
}
> f not installed sass pack:
npm install -D sass-loader node-sass

import { ipcRenderer } from 'electron' produces this error: __dirname is not defined

With this simple vue page:
<template>
<div class="home">
<HelloWorld msg="Welcome to Your Vue.js App"/>
</div>
</template>
<script>
import HelloWorld from '#/components/HelloWorld.vue'
import { ipcRenderer } from 'electron'
export default {
name: 'Home',
components: {
HelloWorld
},
data() {
return {
dato: null
}
},
methods: {
rendererFunct () {
//ipcRenderer.on('setting', (event, arg) => {
//console.log(arg);
//})
}
}
}
</script>
The only presence of import { ipcRenderer } from 'electron' produces the error __dirname is not defined :
Is this problem is something related to webpack configuration or it is due to something else?
This is my webpack.config.js :
import 'script-loader!./script.js';
import webpack from 'webpack';
const path = require('path');
const CopyPlugin = require('copy-webpack-plugin');
module.exports = {
target: ['electron-renderer', 'electron-main', 'electron-preload'],
pluginOptions: {
electronBuilder: {
chainWebpackMainProcess: config => {
config.resolve.alias.set('jsbi', path.join(__dirname, 'node_modules/jsbi/dist/jsbi-cjs.js'));
}
},
},
};
module.exports = {
entry: './src/background.js',
target: 'node',
output: {
path: path.join(__dirname, 'build'),
filename: 'backend.js'
}
}
module.exports = config => {
config.target = "electron-renderer";
return config;
};
module.exports = {
plugins: [
new CopyPlugin({
patterns: [
{ from: 'source', to: 'dest' },
{ from: 'other', to: 'public' },
],
options: {
concurrency: 100,
},
}),
],
};
module.exports = {
module: {
rules: [
{
test: /\.s[ac]ss$/i,
use: [
// Creates `style` nodes from JS strings
'style-loader',
// Translates CSS into CommonJS
'css-loader',
// Compiles Sass to CSS
'sass-loader',
],
},
],
},
};
const supportedLocales = ['en-US', 'it'];
export default const config = {
plugins: [
new webpack.ContextReplacementPlugin(
/date\-fns[\/\\]/,
new RegExp(`[/\\\\\](${supportedLocales.join('|')})[/\\\\\]index\.js$`)
)
]
}
This is vue.config.js :
module.exports = {
configureWebpack: {
// Configuration applied to all builds
},
pluginOptions: {
electronBuilder: {
chainWebpackMainProcess: (config) => {
// Chain webpack config for electron main process only
},
chainWebpackRendererProcess: (config) => {
config.plugin('define').tap((args) => {
args[0]['IS_ELECTRON'] = true
return args
})
},
mainProcessFile: 'src/background.js',
mainProcessWatch: ['src/preload.js'],
}
}
}
module.exports = {
pluginOptions: {
electronBuilder: {
disableMainProcessTypescript: false,
mainProcessTypeChecking: false
}
}
}
Electron: version 9.0.0
webpack: version 4.44.1
System:
OS: Linux 5.4 Ubuntu 18.04.4 LTS (Bionic Beaver)
CPU: (8) x64 Intel(R) Core(TM) i7-4790K CPU # 4.00GHz
Binaries:
Node: 14.5.0 - ~/.nvm/versions/node/v14.5.0/bin/node
Yarn: 1.22.4 - /usr/bin/yarn
npm: 6.14.5 - ~/.nvm/versions/node/v14.5.0/bin/npm
Browsers:
Chrome: 84.0.4147.105
Firefox: 79.0
Looking forward to your kind help.
Marco
__dirname is a NodeJS variable, in recent electron versions, node integration is disabled by default. When opening your BrowserWindow, you should add the following to the options:
webpreferences:{
nodeIntegration: true
}
This is however STRONGLY DISCOURAGED as this opens up security issues.
this seems to solve it for most people (for me sadly enough i now get the next error:
fs.existsSync is not a function)
a better solution i to change your bundler to the correct build mode. You should not be building for node but for web, so target:esnext or something.
if something requires node access, this should be solved by running it in the background thread or the preload scripts.
You can apply the solution described on this post
How to import ipcRenderer in vue.js ? __dirname is not defined
In this way you can call this method from vue files:
window.ipcRenderer.send(channel, args...)
Just make sure you configure preload.js on vue.config.js:
// vue.config.js - project root
module.exports = {
pluginOptions: {
electronBuilder: {
preload: 'src/preload.js' //make sure you have this line added
}
}
}

How do I load environment variables in Vue.js without webpack?

The question has it all, but basically, I set up a new vue app without webpack. When I try to do:
handleSubmit() {
axios
.post(`${process.env.VUE_APP_API_URL}`)
.then(response => (this.info = response));
this.$router.push({ name: "ConversationsList" });
}
It's undefined. In my .env file, I have:
VUE_APP_API_URL=http://localhost:3000
What do I have to do now?
Easy fix. Created a file called vue.config.js:
const webpack = require('webpack')
module.exports = {
configureWebpack: {
plugins: [
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify(process.env.NODE_ENV)
}
})
]
}
}

Testing dynamic component using import() - TypeError: Path must be a string

Please help, I got this error when trying to create test for vue js dynamic component using jest.
I'm using babel-plugin-syntax-dynamic-import and babel-plugin-transform-dynamic-import with babel ver > 7.0.0
● Test suite failed to run
TypeError: Path must be a string. Received undefined
at TestExclude.shouldInstrument (node_modules/test-exclude/index.js:77:31)
at shouldSkip (node_modules/babel-plugin-istanbul/lib/index.js:59:21)
at PluginPass.enter (node_modules/babel-plugin-istanbul/lib/index.js:74:15)
at newFn (node_modules/#babel/core/node_modules/#babel/traverse/lib/visitors.js:193:21)
at NodePath._call (node_modules/#babel/core/node_modules/#babel/traverse/lib/path/context.js:53:20)
at NodePath.call (node_modules/#babel/core/node_modules/#babel/traverse/lib/path/context.js:40:17)
at NodePath.visit (node_modules/#babel/core/node_modules/#babel/traverse/lib/path/context.js:88:12)
at TraversalContext.visitQueue (node_modules/#babel/core/node_modules/#babel/traverse/lib/context.js:118:16)
Here's my my-page.js code:
const MyComponent = () => import('src/components/MyComponent')
...
components: {
MyComponent
},
...
myPage.spec.js code:
import { shallow } from 'vue-test-utils'
import MyPage from '#/pages/MyPage'
describe('MyPage.vue', () => {
let component
beforeEach(() => {
component = shallow(MyPage, {
...
})
jest.resetModules()
jest.clearAllMocks()
})
it('method#isEven', () => {
let result = component.vm.isEven(2)
expect(result).toBe(true)
})
})
jest.conf.js code:
const path = require('path')
module.exports = {
rootDir: path.resolve(__dirname, '../../'),
moduleFileExtensions: [
'js',
'json',
'vue'
],
moduleNameMapper: {
'^#/(.*)$': '<rootDir>/src/$1'
},
transform: {
'^.+\\.js$': '<rootDir>/node_modules/babel-jest',
'.*\\.(vue)$': '<rootDir>/node_modules/vue-jest'
},
testPathIgnorePatterns: [
'<rootDir>/test/e2e'
],
snapshotSerializers: ['<rootDir>/node_modules/jest-serializer-vue'],
setupFiles: ['<rootDir>/test/unit/setup'],
coverageDirectory: '<rootDir>/test/unit/coverage',
collectCoverageFrom: [
'src/**/*.{js,vue}',
'!src/main.js',
'!src/router/index.js',
'!**/node_modules/**'
],
testURL: 'http://localhost/unit-test'
}
Do I need to add more configuration?
No need to use babel-plugin-syntax-dynamic-import, use babel-plugin-dynamic-import-node instead. Implementation can be seen at https://jestjs.io/docs/en/webpack.html#using-with-webpack-2
thanks to https://github.com/facebook/jest/issues/5920