Skip to content

Commit 0e86066

Browse files
authored
Merge pull request #80 from jujaga/test/fe-coverage
Frontend Unit Test Additions
2 parents 1e80163 + 75e216b commit 0e86066

23 files changed

Lines changed: 563 additions & 60 deletions

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ Create, edit and publish forms.
2222
│ ├── docs/ - OpenAPI 3.0 Specification
2323
│ └── forms/ - Models, Controllers, Routes for the forms
2424
└── tests/ - Node.js backend web application tests
25+
components/ - Form.io Custom Components Library
26+
load-test/ - Standalone CHEFS load testing utility
2527
openshift/ - OpenShift-deployment and shared pipeline files
2628
CODE-OF-CONDUCT.md - Code of Conduct
2729
COMPLIANCE.yaml - BCGov PIA/STRA compliance status

app/frontend/src/views/user/Root.vue

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242

4343
<script>
4444
import { mapGetters } from 'vuex';
45-
import rbacService from '@/services/rbacService';
45+
import { rbacService } from '@/services';
4646
4747
export default {
4848
name: 'User',
@@ -53,11 +53,10 @@ export default {
5353
},
5454
computed: {
5555
...mapGetters('auth', [
56-
'hasResourceRoles',
57-
'userName',
56+
'fullName',
5857
'token',
5958
'tokenParsed',
60-
'fullName',
59+
'userName'
6160
]),
6261
},
6362
created() {
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { createLocalVue, shallowMount } from '@vue/test-utils';
2+
import Vuetify from 'vuetify';
3+
4+
import BaseCopyToClipboard from '@/components/base/BaseCopyToClipboard.vue';
5+
6+
const localVue = createLocalVue();
7+
localVue.use(Vuetify);
8+
9+
describe('BaseCopyToClipboard.vue', () => {
10+
it('renders', () => {
11+
const wrapper = shallowMount(BaseCopyToClipboard, {
12+
localVue,
13+
propsData: { copyText: 'test' }
14+
});
15+
16+
expect(wrapper.text()).toMatch('Copy to clipboard');
17+
});
18+
19+
it('clipboardSuccessHandler behaves correctly', () => {
20+
const wrapper = shallowMount(BaseCopyToClipboard, {
21+
localVue,
22+
propsData: { copyText: 'test' }
23+
});
24+
wrapper.vm.clipboardSuccessHandler();
25+
26+
expect(wrapper.vm.clipSnackbar.on).toBeTruthy();
27+
expect(wrapper.vm.clipSnackbar.text).toMatch('Link copied to clipboard');
28+
expect(wrapper.vm.clipSnackbar.color).toEqual('info');
29+
expect(wrapper.emitted().copied).toBeTruthy();
30+
});
31+
32+
it('clipboardErrorHandler behaves correctly', () => {
33+
const wrapper = shallowMount(BaseCopyToClipboard, {
34+
localVue,
35+
propsData: { copyText: 'test' }
36+
});
37+
wrapper.vm.clipboardErrorHandler();
38+
39+
expect(wrapper.vm.clipSnackbar.on).toBeTruthy();
40+
expect(wrapper.vm.clipSnackbar.text).toMatch('Error attempting to copy to clipboard');
41+
expect(wrapper.vm.clipSnackbar.color).toEqual('error');
42+
});
43+
});
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { createLocalVue, shallowMount } from '@vue/test-utils';
2+
import Vuetify from 'vuetify';
3+
4+
import BaseImagePopout from '@/components/base/BaseImagePopout.vue';
5+
6+
const localVue = createLocalVue();
7+
localVue.use(Vuetify);
8+
9+
describe('BaseImagePopout.vue', () => {
10+
it('renders', () => {
11+
const wrapper = shallowMount(BaseImagePopout, {
12+
localVue,
13+
propsData: { src: 'test' }
14+
});
15+
16+
expect(wrapper.html()).toMatch('v-hover');
17+
expect(wrapper.html()).toMatch('v-dialog');
18+
});
19+
});
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import { createLocalVue, shallowMount } from '@vue/test-utils';
2+
import Vuex from 'vuex';
3+
4+
import BaseNotificationBar from '@/components/base/BaseNotificationBar.vue';
5+
6+
const localVue = createLocalVue();
7+
localVue.use(Vuex);
8+
9+
describe('BaseNotificationBar.vue', () => {
10+
let mockDeleteNotification = jest.fn();
11+
let store;
12+
13+
beforeEach(() => {
14+
store = new Vuex.Store({
15+
modules: {
16+
notifications: {
17+
namespaced: true,
18+
actions: {
19+
deleteNotification: mockDeleteNotification
20+
}
21+
}
22+
}
23+
});
24+
});
25+
26+
afterEach(() => {
27+
mockDeleteNotification.mockClear();
28+
});
29+
30+
it('renders', () => {
31+
const msg = 'test';
32+
const wrapper = shallowMount(BaseNotificationBar, {
33+
localVue,
34+
propsData: { notification: { message: msg, type: 'error' } },
35+
store
36+
});
37+
38+
expect(wrapper.html()).toMatch('v-alert');
39+
expect(wrapper.text()).toMatch(msg);
40+
});
41+
42+
it('alertClosed behaves correctly', () => {
43+
const wrapper = shallowMount(BaseNotificationBar, {
44+
localVue,
45+
propsData: { notification: { message: 'test', type: 'error' } },
46+
store
47+
});
48+
wrapper.vm.alertClosed();
49+
50+
expect(mockDeleteNotification).toHaveBeenCalledTimes(1);
51+
});
52+
53+
it('clears timeout before destroy', () => {
54+
const wrapper = shallowMount(BaseNotificationBar, {
55+
localVue,
56+
propsData: { notification: { message: 'test', type: 'error' } },
57+
store
58+
});
59+
wrapper.destroy();
60+
61+
expect(wrapper.vm.timeout).not.toBeNull();
62+
});
63+
});
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { createLocalVue, shallowMount } from '@vue/test-utils';
2+
import Vuex from 'vuex';
3+
4+
import BaseNotificationContainer from '@/components/base/BaseNotificationContainer.vue';
5+
6+
const localVue = createLocalVue();
7+
localVue.use(Vuex);
8+
9+
describe('BaseNotificationContainer.vue', () => {
10+
let store;
11+
12+
beforeEach(() => {
13+
store = new Vuex.Store({
14+
modules: {
15+
notifications: {
16+
namespaced: true,
17+
state: {
18+
notifications: []
19+
}
20+
}
21+
}
22+
});
23+
});
24+
25+
it('renders', () => {
26+
const wrapper = shallowMount(BaseNotificationContainer, {
27+
localVue,
28+
store,
29+
stubs: ['BaseNotificationBar']
30+
});
31+
32+
expect(wrapper.html()).toMatch('notification-container');
33+
});
34+
});
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import { createLocalVue, shallowMount } from '@vue/test-utils';
2+
3+
import { formService } from '@/services';
4+
import Form from '@/views/Form.vue';
5+
6+
const localVue = createLocalVue();
7+
8+
describe('Form.vue', () => {
9+
const mockConsoleError = jest.spyOn(console, 'error');
10+
const readFormSpy = jest.spyOn(formService, 'readForm');
11+
12+
beforeEach(() => {
13+
mockConsoleError.mockReset();
14+
readFormSpy.mockReset();
15+
});
16+
17+
afterAll(() => {
18+
mockConsoleError.mockRestore();
19+
readFormSpy.mockRestore();
20+
});
21+
22+
it('renders without formId', () => {
23+
const wrapper = shallowMount(Form, {
24+
localVue,
25+
stubs: ['router-view'],
26+
});
27+
28+
expect(wrapper.html()).toMatch('router-view');
29+
expect(readFormSpy).toHaveBeenCalledTimes(0);
30+
});
31+
32+
it('renders with formId correctly', async () => {
33+
const name = 'test';
34+
readFormSpy.mockImplementation(() => ({ data: { name: name } }));
35+
const wrapper = shallowMount(Form, {
36+
localVue,
37+
propsData: { formId: '123' },
38+
stubs: ['router-view'],
39+
});
40+
await localVue.nextTick();
41+
42+
expect(wrapper.html()).toMatch('router-view');
43+
expect(readFormSpy).toHaveBeenCalledTimes(1);
44+
expect(wrapper.vm.formName).toMatch(name);
45+
});
46+
47+
it('renders with formId and logs error', async () => {
48+
readFormSpy.mockImplementation(() => {
49+
throw new Error('error');
50+
});
51+
const wrapper = shallowMount(Form, {
52+
localVue,
53+
propsData: { formId: '123' },
54+
stubs: ['router-view'],
55+
});
56+
await localVue.nextTick();
57+
58+
expect(wrapper.html()).toMatch('router-view');
59+
expect(readFormSpy).toHaveBeenCalledTimes(1);
60+
expect(wrapper.vm.formName).toMatch('');
61+
});
62+
});

app/frontend/tests/unit/views/NotFound.spec.js

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,16 @@
1-
import { shallowMount } from '@vue/test-utils';
2-
import Vuetify from 'vuetify';
1+
import { createLocalVue, shallowMount } from '@vue/test-utils';
32
import VueRouter from 'vue-router';
43

54
import NotFound from '@/views/NotFound.vue';
65

7-
describe('NotFound.vue', () => {
8-
let router;
9-
let vuetify;
10-
11-
beforeEach(() => {
12-
router = new VueRouter();
13-
vuetify = new Vuetify();
14-
});
6+
const localVue = createLocalVue();
7+
localVue.use(VueRouter);
158

9+
describe('NotFound.vue', () => {
1610
it('renders', () => {
1711
const wrapper = shallowMount(NotFound, {
18-
vuetify,
19-
router,
20-
stubs: ['router-link', 'router-view']
12+
localVue,
13+
stubs: ['router-link']
2114
});
2215

2316
expect(wrapper.text()).toMatch('404: Page not found. :(');
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { createLocalVue, shallowMount } from '@vue/test-utils';
2+
import User from '@/views/User.vue';
3+
4+
const localVue = createLocalVue();
5+
6+
describe('User.vue', () => {
7+
it('renders', () => {
8+
const wrapper = shallowMount(User, {
9+
localVue,
10+
stubs: ['router-view']
11+
});
12+
13+
expect(wrapper.html()).toMatch('router-view');
14+
});
15+
});
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import { createLocalVue, shallowMount } from '@vue/test-utils';
2+
import Vuex from 'vuex';
3+
4+
import Design from '@/views/form/Design.vue';
5+
6+
const localVue = createLocalVue();
7+
localVue.use(Vuex);
8+
9+
describe('Design.vue', () => {
10+
const mockWindowConfirm = jest.spyOn(window, 'confirm');
11+
const mockFormGetter = jest.fn();
12+
let store;
13+
14+
beforeEach(() => {
15+
store = new Vuex.Store({
16+
modules: {
17+
form: {
18+
namespaced: true,
19+
getters: {
20+
form: mockFormGetter
21+
}
22+
}
23+
}
24+
});
25+
});
26+
27+
afterEach(() => {
28+
mockWindowConfirm.mockReset();
29+
mockFormGetter.mockReset();
30+
});
31+
32+
afterAll(() => {
33+
mockWindowConfirm.mockRestore();
34+
});
35+
36+
it('renders', () => {
37+
const wrapper = shallowMount(Design, {
38+
localVue,
39+
store,
40+
stubs: ['BaseSecure', 'FormDesigner']
41+
});
42+
43+
expect(wrapper.html()).toMatch('basesecure');
44+
});
45+
46+
it('beforeRouteLeave guard works when not dirty', () => {
47+
mockFormGetter.mockReturnValue({ isDirty: false });
48+
const next = jest.fn();
49+
const wrapper = shallowMount(Design, {
50+
localVue,
51+
store,
52+
stubs: ['BaseSecure', 'FormDesigner']
53+
});
54+
Design.beforeRouteLeave.call(wrapper.vm, undefined, undefined, next);
55+
56+
expect(next).toHaveBeenCalledTimes(1);
57+
expect(mockWindowConfirm).toHaveBeenCalledTimes(0);
58+
});
59+
60+
it('beforeRouteLeave guard works when not dirty', () => {
61+
mockFormGetter.mockReturnValue({ isDirty: true });
62+
const next = jest.fn();
63+
const wrapper = shallowMount(Design, {
64+
localVue,
65+
store,
66+
stubs: ['BaseSecure', 'FormDesigner']
67+
});
68+
Design.beforeRouteLeave.call(wrapper.vm, undefined, undefined, next);
69+
70+
expect(next).toHaveBeenCalledTimes(1);
71+
expect(mockWindowConfirm).toHaveBeenCalledTimes(1);
72+
});
73+
});

0 commit comments

Comments
 (0)