Vue test utils not detecting Bootstrap component - vue.js

I'm trying to test a component that uses a child component WarnOnUnsavedModal. I'm not trying to test the child component, however.
The child component uses <b-modal>, and in node_modules, that imports a component called bBtn.
When I try to run my test file, it fails with the following message:
import bBtn from '../button/button';
^^^^
SyntaxError: Unexpected identifier
My test file:
import BootstrapVue, { bBtn } from 'bootstrap-vue';
import { mount, createLocalVue } from '#vue/test-utils';
import ComponentName from '../ComponentName.vue';
const localVue = createLocalVue();
localVue.use(BootstrapVue);
describe('ComponentName', () => {
it('Has props', () => {
const wrapper = mount(ComponentName, {
store,
createLocalVue,
stubs: {
ModalWarnOnSave,
'b-btn': bBtn,
},
propsData: {
resourceType: 'General',
},
});
expect(1 + 1).toBe(2);
});
});
I've tried adding a line like this to the stubs:
stubs: {
ModalWarnOnSave: true,
},
Why isn't that component being picked up here? I've tried swapping out localVue.use() with Vue.use(), to no avail.
What do I need to do to get this test to run? I'm happy to ignore the child file that's causing the problem.

Try this:
import BootstrapVue, { BButton } from 'bootstrap-vue'
bBtn is not the exported name of the b-button component. b-btn is an alias component name when Vue.use'ing BootstrapVue.
See https://bootstrap-vue.js.org/docs/components/button#importing-individual-components

Related

Vue-test-utils loading dependencies - Vue3

I'm developing some tests for single file components in VueJs. These components use font-awesome.
This is my App, as you can see I have fontawesome available for all child components.
import { createApp } from 'vue';
import App from './App.vue';
import "bootstrap/dist/css/bootstrap.min.css";
import "bootstrap";
import { FontAwesomeIcon } from '#fortawesome/vue-fontawesome';
import { fas } from "#fortawesome/free-solid-svg-icons";
import { library } from '#fortawesome/fontawesome-svg-core';
library.add(fas);
createApp(App)
.component("font-awesome-icon", FontAwesomeIcon)
.mount('#app');
Here's a test
import { mount } from '#vue/test-utils'
import ListComponent from '#/components/ListComponent.vue'
import { FontAwesomeIcon } from '#fortawesome/vue-fontawesome';
let someData = [{
name: 'Some person name',
key: '2222222',
moreInfo: [
{title: 'aa'},
{title: 'bb'},
]
},
{
name: 'Some other person name',
key: '12321123,
moreInfo: [
{title: 'cc'},
{title: 'x'},
]
},
}];
let wrapper = mount(ListComponent, {
propsData: {
someData
},
stubs: {
'font-awesome-icon': FontAwesomeIcon
}
});
describe('ListadoImputados.vue', () => {.... tests ....});
Test details are not important, I don't know how to add / include font-awesome-icon in the context so i can avoid getting the following warnings
console.warn node_modules/#vue/runtime-core/dist/runtime-core.cjs.js:40
[Vue warn]: Failed to resolve component: font-awesome-icon
I tried adding this dependency as a mock and stub but no luck. Also importing Fontawesome at the top of the file does not work, the warning is still showing. I was thinking maybe in creating a vue app in the test file and inject the component like this
createApp(App)
.component("font-awesome-icon", FontAwesomeIcon)
.mount('#app');
but I'm copying and pasting code and I'm not sure this is the right way.
Is there a way to add this dependencies to my test context?
I'm using Vue 3, vue-test-utils + jest
In Vue Test Utils v2 (for Vue 3), the stubs mounting option is moved into global.stubs. Also note that a stub does nothing by definition, so stubbing the component only requires providing the component name.
Your mounting options should look like this:
const wrapper = mount(ListComponent, {
global: {
stubs: ['FontAwesomeIcon']
}
})
If for some reason you need the actual component, you could technically provide the component definition as a "stub", but you'd also need to initialize the icons for it as you would in the app's startup:
// assume only `faUserSecret` icon used in `ListComponent`
import { library } from '#fortawesome/fontawesome-svg-core'
import { faUserSecret } from '#fortawesome/free-solid-svg-icons'
import { FontAwesomeIcon } from '#fortawesome/vue-fontawesome'
library.add(faUserSecret)
//...
const wrapper = mount(ListComponent, {
global: {
stubs: { FontAwesomeIcon }
}
})

Can't Test Vue Component because Vue instance not defined

I have a Vue application that uses used Vue Analytics, which creates a this.$ga.page method to do page tracking. Now, I need to test my component, using Jest, but it says this.$ga.page is not defined. I'm initializing Vue Analytics in main.js.
How do I include main.js before the component in the test file? I know we need to add this in a beforeEach method, but I don't know exactly how to do that.
Vue Analytics init in main.js
import VueAnalytics from 'vue-analytics'
Vue.use(VueAnalytics, {
id: 'UA-150437966-1'
})
My Homepage test
import { mount } from '#vue/test-utils'
import Homepage from '../../src/Pages/Homepage'
describe('Homepage', () => {
const wrapper = mount(Homepage)
it('has a button', () => {
expect(wrapper.contains('button')).toBe(true)
})
})
Homepage.vue excerpt
created: function() {
//analytics
this.$ga.page({
page: "/",
title: "Home page",
location: window.location.href
});
}
};
Error I'm getting
Homepage › encountered a declaration exception
TypeError: Cannot read property 'page' of undefined
67 | //analytics
68 |
> 69 | this.$ga.page({
Vue Test Utils recommends setting up your plugins in a localVue to avoid polluting the global Vue. This is what it would look like:
import { localVue, mount } from '#vue/test-utils'
import Homepage from '#/components/Homepage'
localVue.use(VueAnalytics, { id: 'UA-150437966-1' })
describe('Homepage', () => {
let wrapper = null
beforeEach(() => {
wrapper = mount(Homepage, {
localVue,
})
})
// ...
})
On the other hand, if it doesn't matter in your tests that the global Vue is modified, you could setup your Jest 23.x environment with setupTestFrameworkScriptFile:
// jest.config.js
module.exports = {
setupTestFrameworkScriptFile: '<rootDir>/tests/jest-setup.js',
}
And in your setup file, you could initialize Vue Analytics:
// <rootDir>/tests/jest-setup.js
import Vue from 'vue'
import VueAnalytics from 'vue-analytics'
Vue.use(VueAnalytics, {
id: 'UA-150437966-1'
})

Import bootstrap error: Unexpected token, test-utils, working in one test but not another

I'm new to unit testing with jest in vue.
I have added the plugin unit test from here: https://www.npmjs.com/package/#vue/cli-plugin-unit-jest
It have created a test folder with another folder called unit where I have placed 2 files: Footer.spec.js and Main.spec.js. It also created a jest.config.js file.
Footer.spec.js
import '../../src/plugins/fontawesome'
import { mount } from '#vue/test-utils'
import Footer from '../../src/components/Footer/Footer.vue'
import BootstrapVue from 'bootstrap-vue'
import { createLocalVue } from '#vue/test-utils'
describe('Footer', () => {
const localVue = createLocalVue()
localVue.use(BootstrapVue)
const wrapper = mount(Footer, {localVue})
it('has a FooterPhoneNumber', () => {
expect(wrapper.html()).toContain('some HTML')
})
it('has a FooterEmail', () => {
expect(wrapper.html()).toContain('some HTML')
})
})
Main.spec.js
import '../../src/plugins/fontawesome'
import { mount } from '#vue/test-utils'
import Main from '../../src/components/Main.vue'
import BootstrapVueMain from 'bootstrap-vue'
import { createLocalVueMain } from '#vue/test-utils'
describe('Main', () => {
const localVueMain = createLocalVueMain()
localVueMain.use(BootstrapVueMain)
const wrapper = mount(Main, {localVueMain})
it('the modal for browser not compatible', () => {
expect(wrapper.html()).toContain('some HTML')
})
})
From the jest.config.js
transformIgnorePatterns: [
'/node_modules/(?!(bootstrap|bootstrap-vue)/)',
'/node_modules/',
],
I'm getting an error because of 2 imports:
import 'bootstrap/dist/css/bootstrap.css'
| ^
18 | import 'bootstrap-vue/dist/bootstrap-vue.css'
The Footer.spec.js pass the test but the Main.spec.js dosen't.
Any suggestions on how to fix it?
In the Main.spec.js you are importing and using createLocalVueMain(), instead of createLocalVue(). Because of that, BootstrapVue is not registered to localVue instance - because it was not created.

How can you test if an input is focused with Vue Test utils?

I have a directive that focuses on the input. And i want to test this directive. The only issue is i can't find how i can test if the input is focused
This is my simple mock component
<template>
<div v-directive>
<input/>
</div>
</template>
This is my directive:
import Vue from 'vue'
export const focusInput = () => {
return {
inserted(el: any) {
el.getElementsByTagName('input')[0].focus()
}
}
}
export default Vue.directive('focus-input', focusInput())
This is my test:
import Vue from 'vue'
import { mount , createLocalVue} from '#vue/test-utils'
import FocusInputMock from './mockComponents/FocusInputMock.vue'
import {focusInput} from '#/directives/focusInput';
const localVue = createLocalVue()
localVue.directive('directive', focusInput())
test('update content after changing state', () => {
const wrapper = mount(FocusInputMock, {
localVue
})
Vue.nextTick(function () {
expect(wrapper.find('input')) // I have to expect for this input to be focused
});
})
Anyone has any ideas? I've been reading the Jest and Vue utils docs without any success.
While mounting your mock component, pass { attachToDocument: true }
and try this:
let input = wrapper.find('input').element
expect(input).toBe(document.activeElement);

Vue-Test-Utils Unknown custom element: <router-link>

I'm using Jest to run my tests utilizing the vue-test-utils library.
Even though I've added the VueRouter to the localVue instance, it says it can't actually find the router-link component. If the code looks a little funky, it's because I'm using TypeScript, but it should read pretty close to ES6... Main thing is that the #Prop() is the same as passing in props: {..}
Vue component:
<template>
<div>
<div class="temp">
<div>
<router-link :to="temp.url">{{temp.name}}</router-link>
</div>
</div>
</div>
</template>
<script lang="ts">
import Vue from 'vue'
import Component from 'vue-class-component'
import { Prop } from 'vue-property-decorator'
import { Temp } from './Temp'
#Component({
name: 'temp'
})
export default class TempComponent extends Vue {
#Prop() private temp: Temp
}
</script>
<style lang="scss" scoped>
.temp {
padding-top: 10px;
}
</style>
Temp model:
export class Temp {
public static Default: Temp = new Temp(-1, '')
public url: string
constructor(public id: number, public name: string) {
this.id = id
this.name = name
this.url = '/temp/' + id
}
}
Jest test
import { createLocalVue, shallow } from '#vue/test-utils'
import TempComponent from '#/components/Temp.vue'
import { Temp } from '#/components/Temp'
import VueRouter from 'vue-router'
const localVue = createLocalVue()
localVue.use(VueRouter)
describe('Temp.vue Component', () => {
test('renders a router-link tag with to temp.url', () => {
const temp = Temp.Default
temp.url = 'http://some-url.com'
const wrapper = shallow(TempComponent, {
propsData: { temp }
})
const aWrapper = wrapper.find('router-link')
expect((aWrapper.attributes() as any).to).toBe(temp.url)
})
})
What am I missing? The test actually passes, it just throws the warning. In fact, here is the output:
Test Output:
$ jest --config test/unit/jest.conf.js
PASS ClientApp\components\__tests__\temp.spec.ts
Temp.vue Component
√ renders a router-link tag with to temp.url (30ms)
console.error node_modules\vue\dist\vue.runtime.common.js:589
[Vue warn]: Unknown custom element: <router-link> - did you register the
component correctly? For recursive components, make sure to provide the
"name" option.
(found in <Root>)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 4.677s
Ran all test suites.
Done in 6.94s.
Appreciate any help you can give!
Add the router-link stub to the shallow (or shallowMount) method options like this:
const wrapper = shallow(TempComponent, {
propsData: { temp },
stubs: ['router-link']
})
or this way:
import { RouterLinkStub } from '#vue/test-utils';
const wrapper = shallow(TempComponent, {
propsData: { temp },
stubs: {
RouterLink: RouterLinkStub
}
})
The error should go away after you do this.
With Vue 3 and Vue Test Utils Next (v4), it seems you just have to add your router (the return object from createRouter) as a plugin to your mountOptions:
import router from "#/router";
const mountOptions = {
global: {
plugins: [router],
},
};
https://next.vue-test-utils.vuejs.org/api/#global
Or a more full example:
import router from "#/router";
import Button from "#/components/Button.vue";
const mountOptions = {
global: {
mocks: {
$route: "home",
$router: {
push: jest.fn(),
},
},
plugins: [router],
},
};
it("Renders", () => {
const wrapper = shallowMount(Button, mountOptions);
expect(wrapper.get("nav").getComponent({ name: "router-link" })).toExist();
});
Note, in the example above I'm using a project setup with Vue CLI.
Worked for me:
[ Package.json ] file
...
"vue-jest": "^3.0.5",
"vue-router": "~3.1.5",
"vue": "~2.6.11",
"#vue/test-utils": "1.0.0-beta.29",
...
[ Test ] file
import App from '../../src/App';
import { mount, createLocalVue } from '#vue/test-utils';
import VueRouter from 'vue-router';
const localVue = createLocalVue();
localVue.use(VueRouter);
const router = new VueRouter({
routes: [
{
name: 'dashboard',
path: '/dashboard'
}
]
});
describe('Successful test', ()=>{
it('works', ()=>{
let wrapper = mount(App, {
localVue,
router
});
// Here is your assertion
});
});
Or you can try this:
const wrapper = shallow(TempComponent, {
propsData: { temp },
localVue
})