Code splitting with auto-registered dynamically imported Vue components - vue.js

I am trying to use this techniqe. In short I am trying code splitting with auto-registered dynamically imported Vue components. My file structure is js/utils/vue.js and js/components/***.vue.
This is how my Vue components are loaded:
const files = require.context('../components', true, /\.vue$/i, 'lazy').keys();
files.forEach(file => {
Vue.component(file.split('/').pop().split('.')[0], () => import(`${file}`));
});
But it results in an error:
[Vue warn]: Failed to resolve async component: function () {
return webpack_require("./resources/js/utils lazy recursive ^.*$")("".concat(file));
}
Reason: Error: Cannot find module './MainNavbar.vue'
Manually registering the component, while still using dynamic import works.
Vue.component('main-navbar', () => import('../components/MainNavbar.vue'));
Why am I receiving this error?

edit:
I found something here that works for me:
const files = require.context('./', true, /\.vue$/i, 'lazy').keys();
files.forEach(file => {
Vue.component(file.split('/').pop().split('.')[0], () => import(`${file}`));
});
Judging from the error message, your expression () => import(${file}) needs to prepend the context path, since you're not referencing that like in () => import('../components/MainNavbar.vue')
so maybe something like
const componentName = key.split('/').pop().split('.')[0]
Vue.component(file.split('/').pop().split('.')[0], () => import(`../components/${file}`));
works?

Related

UnhandledPromiseRejection when I am trying to spy window object (jest + nuxt js)

I am using jest to spy window object to define my own window.scrollY value for my nuxt js component.
My jest codes:
const winSpy = jest.spyOn(global, 'window', 'get')
winSpy.mockImplementation(() => ({
scrollY: 200
}))
It works fine, but the error below is shown in terminal:
node:internal/process/promises:265
triggerUncaughtException(err, true /* fromPromise */);
^
[UnhandledPromiseRejection: This error originated either by throwing inside of an
async function without a catch block, or by rejecting a promise which was not handled
with .catch(). The promise rejected with the reason "TypeError: Cannot read
properties of undefined (reading 'get')".] {
What is the problem?
Can anyone help me to fix that?
You need to include the original window properties in the mockImplementation:
describe('MyComponent', () => {
it('reads scrollY', () => {
const originalWindow = { ...window }
const windowSpy = jest.spyOn(global, 'window', 'get')
windowSpy.mockImplementation(() => ({
...originalWindow, 👈
scrollY: 200,
}))
const wrapper = shallowMount(MyComponent)
expect(wrapper.vm.top).toBe(200)
})
})
demo

How do I use Vue.use with dynamic imports?

Currently I have
import { SchedulePlugin } from "#syncfusion/ej2-vue-schedule";
Vue.use(SchedulePlugin);
I would like to change this to a dynamic import.
I've changed the import to:
const { SchedulePlugin } = () => import("#syncfusion/ej2-vue-schedule");
but have been unable to find the syntax for the corresponding changes I need to make to Vue.use.
What is the correct syntax to use?
The dynamic import syntax you have is for specifying async components, where Vue internally resolves them. For Vue plugins, you have to resolve the module yourself before passing it on to Vue.use(). The import() method returns a Promise, so you can await the result of the module loading in the context of an async function:
const loadPlugins = async () => {
const { SchedulePlugin } = await import("#syncfusion/ej2-vue-schedule")
Vue.use(SchedulePlugin)
}
loadPlugins()
Note the plugins should be loaded before the app to ensure the plugin's effects are available to the app:
loadPlugins().then(() => {
new Vue({
render: (h) => h(App)
}).$mount("#app")
})
demo

How to initialize manually next.js app (for testing purpose)?

I try to test my web services, hosted in my Next.js app and I have an error with not found Next.js configuration.
My web service are regular one, stored in the pages/api directory.
My API test fetches a constant ATTACKS_ENDPOINT thanks to this file:
/pages/api/tests/api.spec.js
import { ATTACKS_ENDPOINT } from "../config"
...
describe("endpoints", () => {
beforeAll(buildOptionsFetch)
it("should return all attacks for attacks endpoint", async () => {
const response = await fetch(API_URL + ATTACKS_ENDPOINT, headers)
config.js
import getConfig from "next/config"
const { publicRuntimeConfig } = getConfig()
export const API_URL = publicRuntimeConfig.API_URL
My next.config.js is present and is used properly by the app when started.
When the test is run, this error is thrown
TypeError: Cannot destructure property `publicRuntimeConfig` of 'undefined' or 'null'.
1 | import getConfig from "next/config"
2 |
> 3 | const { publicRuntimeConfig } = getConfig()
I looked for solutions and I found this issue which talks about _manually initialise__ next app.
How to do that, given that I don't test React component but API web service ?
I solved this problem by creating a jest.setup.js file and adding this line of code
First add jest.setup.js to jest.config.js file
// jest.config.js
module.exports = {
// Your config
setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
};
AND then
// jest.setup.js
jest.mock('next/config', () => () => ({
publicRuntimeConfig: {
YOUR_PUBLIC_VARIABLE: 'value-of-env' // Change this line and copy your env
}
}))
OR
// jest.setup.js
import { setConfig } from 'next/config'
import config from './next.config'
// Make sure you can use "publicRuntimeConfig" within tests.
setConfig(config)
The problem I faced with testing with Jest was that next was not being initialized as expected. My solution was to mock the next module... You can try this:
/** #jest-environment node */
jest.mock('next');
import next from 'next';
next.mockReturnValue({
prepare: () => Promise.resolve(),
getRequestHandler: () => (req, res) => res.status(200),
getConfig: () => ({
publicRuntimeConfig: {} /* This is where you import the mock values */
})
});
Read about manual mocks here: https://jestjs.io/docs/en/manual-mocks
In my case, I had to:
Create a jest.setup.js file and
setConfig({
...config,
publicRuntimeConfig: {
BASE_PATH: '/',
SOME_KEY: 'your_value',
},
serverRuntimeConfig: {
YOUR_KEY: 'your_value',
},
});
Then add this in your jest.config.js file:
setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],

Jest Mocking Permissions of Expo TypeError: Cannot read property 'askAsync' of undefined

I'm mocking expo and the Permissions module, but when calling Permissions.AskAsync Permissions is undefined.
Problem looks like this question. Using Jest to mock named imports
Used the provided answer, but did not work.
I have mocked the axios, which works. Doing the same for the expo module does not work.
The function I want to test:
checkPermission = async () => {
const {statusCamera} = await Permissions.askAsync(Permissions.CAMERA);
// console.log(statusCamera);
this.setState({cameraPermission: statusCamera});
const {statusCameraRoll} = await Permissions.askAsync(Permissions.CAMERA_ROLL);
this.setState({cameraRollPermission: statusCameraRoll});
};
The test:
describe("Test the Permission function", () => {
it('should return rejected permission.', async function () {
const wrapper = shallow(<Photo2/>);
const instance = wrapper.instance();
await instance.checkPermission();
expect(instance.state("cameraPermission")).toBeFalsy();
});
});
The mock I use for expo:
jest.mock('expo', ()=>({
Permissions: {
askAsync: jest.fn()
}
}))
and tried
(In file mocks/expo.js)
export default {
Permissions: {
askAsync: jest.fn(() => {
return "SOMETHING"
})
}
}
and tried
(In file mocks/expo.js)
jest.mock('expo', ()=>({
Permissions: {
askAsync: jest.fn()
}
}));
Error: "TypeError: Cannot read property 'askAsync' of undefined"
This error occures on line where Permissions.askAsyc is called. So Permissions is undefined. (Also checked it with console.log(Permissions)
I expected the instance.state("cameraPermission") to be falsy, but it crashes before it comes to that line.
Since expo change the packages to be import * as Permissions from 'expo-permissions';
You just need to create "mocks/expo-permissions.js" and have it has:
export const getAsync = jest.fn(permissions => Promise.resolve());
export const askAsync = jest.fn(permissions => Promise.resolve());
teerryn's answer is correct and is a good start. To add some more details:
Unless you've configured different roots for Jest, you should place your mock file in __mocks__/expo-permissions.js where __mocks__ is a directory at the same level as your node_modules folder. See Jest docs on mocking node modules.
The permissions argument being passed in will be undefined due to the way we're mocking the module, so you'll need to mock the permission types you want to use. Just need something simple like export const CAMERA_ROLL = 'camera_roll';
If you want to respond differently based on the permission type passed in (for example, allow Permissions.CAMERA but deny Permissions.CAMERA_ROLL and all other types), you can mock the implementation of the askAsync function. For example, your __mocks__/expo-permissions.js file would look like this:
export const CAMERA = 'camera';
export const CAMERA_ROLL = 'camera_roll';
export const askAsync = jest.fn().mockImplementation((permissionType) => {
const responseData = permissionType === CAMERA ? { status: 'granted' } : { status: 'undetermined' }; // you could also pass `denied` instead of `undetermined`
return Promise.resolve(responseData);
});
The problem is that you are handling async tests incorrectly (your checkPermission() function is async). There are several ways you can tell jest that you want to test an async function. Here are a few ways.
Here is a quick solution to your problem:
...
import { Permissions } from 'expo';
...
jest.mock('expo', () => ({
Permissions: {
askAsync: jest.fn(),
}
}));
...
describe("Test the Permission function", () => {
it('should return rejected permission.', () => {
Permissions.askAsync.mockImplementation( permission => { return {status: 'granted'}; } ); // if you want to add some sort of custom functionality
const wrapper = shallow(<Photo2/>);
const instance = wrapper.instance();
return instance.checkPermission().then(data => {
expect(instance.state("cameraPermission")).toBeFalsy();
});
});
});

How to unit test Vue.js components that use nuxt-i18n

If I try to run the thing below (with yarn run jest), I get TypeError: _vm.$t is not a function, because SearchField is using a translation ("$t('search')").
import { mount } from "#vue/test-utils";
import SearchField from "#/components/ui/SearchField";
describe("SearchField", () => {
const wrapper = mount(SearchField);
it("renders correctly", () => {
expect(wrapper.element).toMatchSnapshot();
});
});
If I add the following three lines at the beginning, I get TypeError: Cannot read property '_t' of undefined instead.
import Vue from "vue";
import VueI18n from "vue-i18n";
Vue.use(VueI18n);
nuxt-i18n is an external library, not your own code, so the test good practices ask us to just mock the translation library and its needed functions ($t in this case).
The following code should solve your problem:
describe("SearchField", () => {
const wrapper = mount(SearchField);
it("renders correctly", () => {
mocks: {
$t: (msg) => msg
}
expect(wrapper.element).toMatchSnapshot();
});
});
More information on this approach can be found here.