diff --git a/frontend/package-lock.json b/frontend/package-lock.json index bcb0d30c..08a3c2f9 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -37,6 +37,7 @@ "typescript": "~5.2.2", "vite": "^4.3.9", "vitest": "^0.32.4", + "vitest-fetch-mock": "^0.2.2", "vue-cli-plugin-vuetify": "~2.5.8", "vue-tsc": "^1.6.5" } @@ -3501,6 +3502,15 @@ "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", "dev": true }, + "node_modules/cross-fetch": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.8.tgz", + "integrity": "sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg==", + "dev": true, + "dependencies": { + "node-fetch": "^2.6.12" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -6892,6 +6902,48 @@ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", "dev": true }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dev": true, + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-fetch/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true + }, + "node_modules/node-fetch/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true + }, + "node_modules/node-fetch/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dev": true, + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/node-gyp": { "version": "9.4.0", "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-9.4.0.tgz", @@ -10417,6 +10469,21 @@ } } }, + "node_modules/vitest-fetch-mock": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/vitest-fetch-mock/-/vitest-fetch-mock-0.2.2.tgz", + "integrity": "sha512-XmH6QgTSjCWrqXoPREIdbj40T7i1xnGmAsTAgfckoO75W1IEHKR8hcPCQ7SO16RsdW1t85oUm6pcQRLeBgjVYQ==", + "dev": true, + "dependencies": { + "cross-fetch": "^3.0.6" + }, + "engines": { + "node": ">=14.14.0" + }, + "peerDependencies": { + "vitest": ">=0.16.0" + } + }, "node_modules/vue": { "version": "3.3.4", "resolved": "https://registry.npmjs.org/vue/-/vue-3.3.4.tgz", diff --git a/frontend/package.json b/frontend/package.json index f840a8ae..53e94b77 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -43,6 +43,7 @@ "typescript": "~5.2.2", "vite": "^4.3.9", "vitest": "^0.32.4", + "vitest-fetch-mock": "^0.2.2", "vue-cli-plugin-vuetify": "~2.5.8", "vue-tsc": "^1.6.5" } diff --git a/frontend/src/components/HeaderDetailPage.vue b/frontend/src/components/HeaderDetailPage.vue index 7c521099..f924913f 100644 --- a/frontend/src/components/HeaderDetailPage.vue +++ b/frontend/src/components/HeaderDetailPage.vue @@ -1,14 +1,14 @@ - + diff --git a/frontend/src/components/SearchBar.vue b/frontend/src/components/SearchBar.vue index a1c7557c..a718c62b 100644 --- a/frontend/src/components/SearchBar.vue +++ b/frontend/src/components/SearchBar.vue @@ -40,7 +40,11 @@ const props = withDefaults(defineProps(), { > - + search diff --git a/frontend/src/components/__tests__/HeaderDefault.spec.ts b/frontend/src/components/__tests__/HeaderDefault.spec.ts index e6cfb70f..b409c194 100644 --- a/frontend/src/components/__tests__/HeaderDefault.spec.ts +++ b/frontend/src/components/__tests__/HeaderDefault.spec.ts @@ -1,48 +1,24 @@ -import { describe, it, expect } from 'vitest' +import { describe, it, expect, vi } from 'vitest' import { mount } from '@vue/test-utils' import HeaderDefault from '../HeaderDefault.vue' import { createVuetify } from 'vuetify' import * as components from 'vuetify/components' import * as directives from 'vuetify/directives' +import { createRouter, createWebHistory } from 'vue-router' +import { routes } from '@/router' const vuetify = createVuetify({ components, directives }) -import { createRouter, createWebHistory } from 'vue-router' -import HomeView from '@/views/HomeView.vue' -import AboutView from '@/views/AboutView.vue' -import ContactView from '@/views/ContactView.vue' -import GeneDetailsView from '@/views/GeneDetailView.vue' - const router = createRouter({ history: createWebHistory(import.meta.env.BASE_URL), - routes: [ - { - path: '/', - name: 'home', - component: HomeView - }, - { - path: '/about', - name: 'about', - component: AboutView - }, - { - path: '/contact', - name: 'contact', - component: ContactView - }, - { - path: '/gene/:searchTerm', - name: 'gene', - component: GeneDetailsView, - props: (route) => ({ searchTerm: route.params.searchTerm }) - } - ] + routes: routes }) +// Mock router push +router.push = vi.fn() global.ResizeObserver = require('resize-observer-polyfill') @@ -61,8 +37,11 @@ describe('HeaderDefault.vue', () => { } } ) + const logo = wrapper.find('#logo') + const title = wrapper.find('a[href="/"]') expect(logo.exists()).toBe(true) + expect(title.text()).toBe('Explanation and Evaluation of Variants') }) it('renders the navigation links', () => { @@ -79,6 +58,7 @@ describe('HeaderDefault.vue', () => { } } ) + const aboutLink = wrapper.find('#about') const contactLink = wrapper.find('#contact') expect(aboutLink.exists()).toBe(true) diff --git a/frontend/src/components/__tests__/HeaderDetailPage.spec.ts b/frontend/src/components/__tests__/HeaderDetailPage.spec.ts index 43d2b507..9d360fbd 100644 --- a/frontend/src/components/__tests__/HeaderDetailPage.spec.ts +++ b/frontend/src/components/__tests__/HeaderDetailPage.spec.ts @@ -1,94 +1,90 @@ import { describe, expect, it, vi } from 'vitest' -import { useRouter } from 'vue-router' +import { mount } from '@vue/test-utils' +import { createRouter, createWebHistory } from 'vue-router' +import { routes } from '@/router' import { createTestingPinia } from '@pinia/testing' -import { mount, RouterLinkStub } from '@vue/test-utils' import { useGeneInfoStore } from '@/stores/geneInfo' import { createVuetify } from 'vuetify' import * as components from 'vuetify/components' import * as directives from 'vuetify/directives' import HeaderDetailPage from '../HeaderDetailPage.vue' +import { StoreState } from '@/stores/geneInfo' -// const vuetify = createVuetify({ -// components, -// directives, -// }) +const vuetify = createVuetify({ + components, + directives +}) -// vi.mock('vue-router', () => ({ -// useRouter: () => ({ -// push: vi.fn() -// }) -// })) +const router = createRouter({ + history: createWebHistory(import.meta.env.BASE_URL), + routes: routes +}) +// Mock router push +router.push = vi.fn() -// const makeWrapper = (geneData = {}) => { -// return mount(HeaderDetailPage, { -// // shallow: true, -// global: { -// plugins: [ -// vuetify, -// // createTestingPinia({ -// // initialState: { data: geneData }, -// // createSpy: vi.fn() -// // }) -// ] -// } -// }) -// } +global.ResizeObserver = require('resize-observer-polyfill') describe('HeaderDetailPage', async () => { - const vuetify = createVuetify({ - components, - directives - }) + it('renders the gene symbol and nav links', () => { + const geneData = { + storeState: 'active', + geneSymbol: 'BRCA1', + geneInfo: { + symbol: 'BRCA1', + name: 'Test Gene', + hgncId: '12345', + ensemblId: 'ENSG00000000000001', + entrezId: '12345' + } + } - // vi.mock('vue-router', () => ({ - // useRouter: () => ({ - // push: vi.fn() - // }) - // })) + const wrapper = mount( + { + template: '' + }, + { + global: { + plugins: [vuetify, router, createTestingPinia({ createSpy: vi.fn() })], + components: { + HeaderDetailPage + } + } + } + ) - // it('renders the gene symbol and nav links', () => { - // const geneData = { - // geneSymbol: 'BRCA1', - // geneInfo: { - // symbol: 'BRCA1', - // name: 'Test Gene', - // hgncId: '12345', - // ensemblId: 'ENSG00000000000001', - // entrezId: '12345' - // } - // } + const store = useGeneInfoStore() + store.storeState = StoreState.Active + store.geneSymbol = geneData.geneSymbol + store.geneInfo = JSON.parse(JSON.stringify(geneData.geneInfo)) - // const wrapper = makeWrapper(geneData) - // const store = useGeneInfoStore() - // const logo = wrapper.find('#logo') - // const aboutLink = wrapper.find('v-btn[to="/about"]') - // const contactLink = wrapper.find('v-btn[to="/contact"]') - // expect(logo.exists()).toBe(true) - // expect(aboutLink.exists()).toBe(true) - // expect(contactLink.exists()).toBe(true) - // }) + const logo = wrapper.find('#logo') + const aboutLink = wrapper.find('#about') + const contactLink = wrapper.find('#contact') + expect(logo.exists()).toBe(true) + expect(aboutLink.exists()).toBe(true) + expect(contactLink.exists()).toBe(true) + }) it('redirects if gene data is null', async () => { - // const geneData = {geneSymbol: '', geneInfo: null} - // const wrapper = makeWrapper(geneData) + const store = useGeneInfoStore() + store.storeState = StoreState.Initial + store.geneSymbol = null + store.geneInfo = null + const wrapper = mount( { - template: '' + template: '' }, { global: { - plugins: [vuetify], + plugins: [vuetify, router, createTestingPinia({ createSpy: vi.fn() })], components: { HeaderDetailPage - }, - stubs: { - RouterLink: RouterLinkStub } } } ) - console.log(wrapper.html()) - // const router = useRouter() - // expect(router.push).toHaveBeenCalled() + + expect(router.push).toHaveBeenCalled() }) }) diff --git a/frontend/src/components/__tests__/SearchBar.spec.ts b/frontend/src/components/__tests__/SearchBar.spec.ts index 9b485793..e70f9985 100644 --- a/frontend/src/components/__tests__/SearchBar.spec.ts +++ b/frontend/src/components/__tests__/SearchBar.spec.ts @@ -1,47 +1,53 @@ -import { describe, it, expect } from 'vitest' - +import { describe, it, expect, vi } from 'vitest' import { mount } from '@vue/test-utils' +import { createRouter, createWebHistory } from 'vue-router' +import { routes } from '@/router' +import { createVuetify } from 'vuetify' +import * as components from 'vuetify/components' +import * as directives from 'vuetify/directives' import SearchBar from '../SearchBar.vue' +const vuetify = createVuetify({ + components, + directives +}) + +const router = createRouter({ + history: createWebHistory(import.meta.env.BASE_URL), + routes: routes +}) +// Mock router push +router.push = vi.fn() + +global.ResizeObserver = require('resize-observer-polyfill') + describe('SearchBar.vue', () => { it('renders the search bar with the correct default props', () => { - const wrapper = mount(SearchBar) - const textField = wrapper.find('v-text-field') - const select = wrapper.find('v-select') - const searchButton = wrapper.find('v-btn') + const wrapper = mount( + { + template: '' + }, + { + global: { + plugins: [vuetify, router], + components: { + SearchBar + } + } + } + ) + + const textField = wrapper.find('.v-text-field') + const select = wrapper.find('.v-select') + const searchButton = wrapper.find('#search') expect(textField.exists()).toBe(true) expect(select.exists()).toBe(true) expect(searchButton.exists()).toBe(true) - expect(textField.attributes('label')).toMatch('Enter search term') - expect(select.attributes('label')).toMatch('Genome Release') - expect(select.attributes('item-title')).toMatch('label') - expect(select.attributes('item-value')).toMatch('value') - expect(select.attributes('model-value')).toMatch('grch37') - expect(searchButton.text()).toMatch('search') - }) - - it.skip('emits events correctly', () => { - const wrapper = mount(SearchBar) - const textField = wrapper.find('v-text-field') - const select = wrapper.find('v-select') - const searchButton = wrapper.find('v-btn') - - // Simulate input in the text field and check if event is emitted - textField.setValue('sample term') - textField.trigger('input') - expect(wrapper.emitted('update:searchTerm')).toBeTruthy() - expect(wrapper.emitted('update:searchTerm')![0][0]).toBe('sample term') - - // Simulate selecting an option in the select component and check if event is emitted - select.setValue('grch38') - select.trigger('update:model-value', 'grch38') - expect(wrapper.emitted('update:genomeRelease')).toBeTruthy() - expect(wrapper.emitted('update:genomeRelease')![0][0]).toBe('grch38') - - // Simulate clicking the search button and check if event is emitted - searchButton.trigger('click') - expect(wrapper.emitted('clickSearch')).toBeTruthy() - expect(wrapper.emitted('clickSearch')![0][0]).toBe('sample term') - expect(wrapper.emitted('clickSearch')![0][1]).toBe('grch38') + expect(textField.html()).toMatch('Enter search term') + expect(select.html()).toMatch('Genome Release') + expect(select.html()).toMatch('label') + expect(select.html()).toMatch('value') + expect(select.html()).toMatch('GRCh37') + expect(searchButton.html()).toMatch('search') }) }) diff --git a/frontend/src/router/index.ts b/frontend/src/router/index.ts index dd2a2120..6bedcd75 100644 --- a/frontend/src/router/index.ts +++ b/frontend/src/router/index.ts @@ -5,31 +5,35 @@ import AboutView from '@/views/AboutView.vue' import ContactView from '@/views/ContactView.vue' import GeneDetailsView from '@/views/GeneDetailView.vue' +const routes = [ + { + path: '/', + name: 'home', + component: HomeView + }, + { + path: '/about', + name: 'about', + component: AboutView + }, + { + path: '/contact', + name: 'contact', + component: ContactView + }, + { + path: '/gene/:searchTerm', + name: 'gene', + component: GeneDetailsView, + props: (route) => ({ searchTerm: route.params.searchTerm }) + } +] + const router = createRouter({ history: createWebHistory(import.meta.env.BASE_URL), - routes: [ - { - path: '/', - name: 'home', - component: HomeView - }, - { - path: '/about', - name: 'about', - component: AboutView - }, - { - path: '/contact', - name: 'contact', - component: ContactView - }, - { - path: '/gene/:searchTerm', - name: 'gene', - component: GeneDetailsView, - props: (route) => ({ searchTerm: route.params.searchTerm }) - } - ] + routes }) +export { routes } + export default router diff --git a/frontend/src/stores/__tests__/geneInfo.spec.ts b/frontend/src/stores/__tests__/geneInfo.spec.ts index bd6dfd16..a50db4b5 100644 --- a/frontend/src/stores/__tests__/geneInfo.spec.ts +++ b/frontend/src/stores/__tests__/geneInfo.spec.ts @@ -1,24 +1,15 @@ import { beforeEach, describe, it, expect, vi } from 'vitest' import { setActivePinia, createPinia } from 'pinia' import { useGeneInfoStore, StoreState } from '../geneInfo' +import createFetchMock from 'vitest-fetch-mock' -const mockFetchGeneInfo = (hgncId: string) => { - expect(hgncId).toBe('BRCA1') - return Promise.resolve({ gene: 'info' }) -} - -class MockAnnonarsClient { - fetchGeneInfo = mockFetchGeneInfo -} - -// vi.mock('@/api/annonars', () => ({ -// const Client = vi.fn().mockImplementation(MockAnnonarsClient) -// return { Client } -// })) +const fetchMocker = createFetchMock(vi) describe('geneInfo Store', () => { beforeEach(() => { setActivePinia(createPinia()) + fetchMocker.enableMocks() + fetchMocker.resetMocks() }) it('should have initial state', () => { @@ -29,22 +20,22 @@ describe('geneInfo Store', () => { expect(store.geneInfo).toBe(null) }) - it.skip('should clear state', () => { + it('should clear state', () => { const store = useGeneInfoStore() - store.storeState = StoreState.Active store.geneSymbol = 'BRCA1' - store.geneInfo = { gene: 'info' } + store.geneInfo = JSON.parse(JSON.stringify({ gene: 'info' })) - store.$clear() + store.clearData() expect(store.storeState).toBe(StoreState.Initial) expect(store.geneSymbol).toBe(null) expect(store.geneInfo).toBe(null) }) - it.skip('should load data', async () => { + it('should load data', async () => { const store = useGeneInfoStore() + fetchMocker.mockResponseOnce(JSON.stringify({ genes: { BRCA1: { gene: 'info' } } })) await store.loadData('BRCA1') @@ -54,33 +45,30 @@ describe('geneInfo Store', () => { }) it('should fail to load data with invalid request', async () => { + // Disable error logging + vi.spyOn(console, 'error').mockImplementation(vi.fn()) const store = useGeneInfoStore() + fetchMocker.mockResponseOnce(JSON.stringify({ foo: 'bar' }), { status: 400 }) - await store.loadData('123') + await store.loadData('invalid') expect(store.storeState).toBe(StoreState.Error) expect(store.geneSymbol).toBe(null) expect(store.geneInfo).toBe(null) }) - it.skip('should not load data if gene symbol is the same', async () => { + it('should not load data if gene symbol is the same', async () => { const store = useGeneInfoStore() - - const mockFetch = (url: string, options: any) => { - expect(url).toBe('/proxy/annonars/genes/info?hgnc-id=BRCA1') - expect(options.method).toBe('GET') - return Promise.resolve({ - json: () => Promise.resolve({ gene: 'info' }) - }) - } - - globalThis.fetch = mockFetch + fetchMocker.mockResponse(JSON.stringify({ genes: { BRCA1: { gene: 'info' } } })) - await store.loadData('BRCA1') await store.loadData('BRCA1') expect(store.storeState).toBe(StoreState.Active) expect(store.geneSymbol).toBe('BRCA1') expect(store.geneInfo).toEqual({ gene: 'info' }) + + await store.loadData('BRCA1') + + expect(fetchMocker.mock.calls.length).toBe(1) }) }) diff --git a/frontend/src/stores/geneInfo.ts b/frontend/src/stores/geneInfo.ts index 736e4646..5c5ee8c0 100644 --- a/frontend/src/stores/geneInfo.ts +++ b/frontend/src/stores/geneInfo.ts @@ -30,7 +30,7 @@ export const useGeneInfoStore = defineStore('geneInfo', () => { geneInfo.value = JSON.parse(localStorage.getItem('geneInfo')!) } - const $clear = () => { + function clearData() { storeState.value = StoreState.Initial geneSymbol.value = null geneInfo.value = null @@ -43,7 +43,7 @@ export const useGeneInfoStore = defineStore('geneInfo', () => { } // Clear against artifact - $clear() + clearData() // Load data via API storeState.value = StoreState.Loading @@ -65,6 +65,7 @@ export const useGeneInfoStore = defineStore('geneInfo', () => { storeState, geneSymbol, geneInfo, - loadData + loadData, + clearData } }) diff --git a/frontend/src/views/__tests__/AboutView.spec.ts b/frontend/src/views/__tests__/AboutView.spec.ts index 59d294e2..c11acc74 100644 --- a/frontend/src/views/__tests__/AboutView.spec.ts +++ b/frontend/src/views/__tests__/AboutView.spec.ts @@ -1,8 +1,67 @@ -import { describe, it } from 'vitest' - +import { describe, expect, it, vi } from 'vitest' import { mount } from '@vue/test-utils' +import { createRouter, createWebHistory } from 'vue-router' +import { routes } from '@/router' +import { createVuetify } from 'vuetify' +import * as components from 'vuetify/components' +import * as directives from 'vuetify/directives' import AboutView from '../AboutView.vue' -describe('AboutView.vue', () => { - it.todo('renders the about page') +const vuetify = createVuetify({ + components, + directives +}) + +const router = createRouter({ + history: createWebHistory(import.meta.env.BASE_URL), + routes: routes +}) +// Mock router push +router.push = vi.fn() + +global.ResizeObserver = require('resize-observer-polyfill') + +describe('AboutView', async () => { + it('renders the header', () => { + const wrapper = mount( + { + template: '' + }, + { + global: { + plugins: [vuetify, router], + components: { + AboutView + } + } + } + ) + + const logo = wrapper.find('#logo') + const aboutLink = wrapper.find('#about') + const contactLink = wrapper.find('#contact') + expect(logo.exists()).toBe(true) + expect(aboutLink.exists()).toBe(true) + expect(contactLink.exists()).toBe(true) + }) + + it('renders the main content', () => { + const wrapper = mount( + { + template: '' + }, + { + global: { + plugins: [vuetify, router], + components: { + AboutView + } + } + } + ) + + const mainContent = wrapper.find('.about-view') + expect(mainContent.exists()).toBe(true) + expect(mainContent.html()).toMatch('REEV: Explanation and Evaluation of Variants') + }) }) diff --git a/frontend/src/views/__tests__/ContactView.spec.ts b/frontend/src/views/__tests__/ContactView.spec.ts index aa41151b..3d84fe80 100644 --- a/frontend/src/views/__tests__/ContactView.spec.ts +++ b/frontend/src/views/__tests__/ContactView.spec.ts @@ -1,8 +1,73 @@ -import { describe, it } from 'vitest' - +import { describe, expect, it, vi } from 'vitest' import { mount } from '@vue/test-utils' +import { createRouter, createWebHistory } from 'vue-router' +import { routes } from '@/router' +import { createVuetify } from 'vuetify' +import * as components from 'vuetify/components' +import * as directives from 'vuetify/directives' import ContactView from '../ContactView.vue' -describe('ContactView.vue', () => { - it.todo('renders the contact page') +const vuetify = createVuetify({ + components, + directives +}) + +const router = createRouter({ + history: createWebHistory(import.meta.env.BASE_URL), + routes: routes +}) +// Mock router push +router.push = vi.fn() + +global.ResizeObserver = require('resize-observer-polyfill') + +describe('ContactView', async () => { + it('renders the header', () => { + const wrapper = mount( + { + template: '' + }, + { + global: { + plugins: [vuetify, router], + components: { + ContactView + } + } + } + ) + + const logo = wrapper.find('#logo') + const aboutLink = wrapper.find('#about') + const contactLink = wrapper.find('#contact') + expect(logo.exists()).toBe(true) + expect(aboutLink.exists()).toBe(true) + expect(contactLink.exists()).toBe(true) + }) + + it('renders the main content', () => { + const wrapper = mount( + { + template: '' + }, + { + global: { + plugins: [vuetify, router], + components: { + ContactView + } + } + } + ) + + const mainContent = wrapper.find('.contact-view') + const githubLink = wrapper.find('.mdi-github') + const emailLink = wrapper.find('.mdi-email') + expect(mainContent.exists()).toBe(true) + expect(mainContent.html()).toMatch( + 'Feel free to reach out to us through any of the following channels:' + ) + expect(githubLink.exists()).toBe(true) + expect(emailLink.exists()).toBe(true) + }) }) diff --git a/frontend/src/views/__tests__/GeneDetailView.spec.ts b/frontend/src/views/__tests__/GeneDetailView.spec.ts new file mode 100644 index 00000000..590a2662 --- /dev/null +++ b/frontend/src/views/__tests__/GeneDetailView.spec.ts @@ -0,0 +1,115 @@ +import { describe, expect, it, vi } from 'vitest' +import { mount } from '@vue/test-utils' +import { createRouter, createWebHistory } from 'vue-router' +import { routes } from '@/router' +import { createTestingPinia } from '@pinia/testing' +import { useGeneInfoStore } from '@/stores/geneInfo' +import { createVuetify } from 'vuetify' +import * as components from 'vuetify/components' +import * as directives from 'vuetify/directives' +import GeneDetailView from '../GeneDetailView.vue' +import { StoreState } from '@/stores/geneInfo' +import { before, beforeEach } from 'node:test' + +const vuetify = createVuetify({ + components, + directives +}) + +const router = createRouter({ + history: createWebHistory(import.meta.env.BASE_URL), + routes: routes +}) +// Mock router push +router.push = vi.fn() + +global.ResizeObserver = require('resize-observer-polyfill') + +describe('GeneDetailView', async () => { + beforeEach(() => { + createTestingPinia({ createSpy: vi.fn() }) + }) + it.skip('renders the header', () => { + const geneData = { + storeState: 'active', + geneSymbol: 'BRCA1', + geneInfo: { + genes: { + BRCA1: { + symbol: 'BRCA1', + name: 'Test Gene', + hgncId: '12345', + ensemblId: 'ENSG00000000000001', + entrezId: '12345' + } + } + } + } + + const store = useGeneInfoStore() + store.storeState = StoreState.Active + store.geneSymbol = geneData.geneSymbol + store.geneInfo = JSON.parse(JSON.stringify(geneData.geneInfo)) + + const wrapper = mount( + { + template: '' + }, + { + global: { + plugins: [vuetify, router, createTestingPinia({ createSpy: vi.fn() })], + components: { + GeneDetailView + } + } + } + ) + + const logo = wrapper.find('#logo') + const aboutLink = wrapper.find('#about') + const contactLink = wrapper.find('#contact') + expect(logo.exists()).toBe(true) + expect(aboutLink.exists()).toBe(true) + expect(contactLink.exists()).toBe(true) + }) + + it.skip('renders the search bar', () => { + const geneData = { + storeState: 'active', + geneSymbol: 'BRCA1', + geneInfo: { + symbol: 'BRCA1', + name: 'Test Gene', + hgncId: '12345', + ensemblId: 'ENSG00000000000001', + entrezId: '12345' + } + } + + const wrapper = mount( + { + template: '' + }, + { + global: { + plugins: [vuetify, router, createTestingPinia({ createSpy: vi.fn() })], + components: { + GeneDetailView + } + } + } + ) + + const store = useGeneInfoStore() + store.storeState = StoreState.Active + store.geneSymbol = geneData.geneSymbol + store.geneInfo = JSON.parse(JSON.stringify(geneData.geneInfo)) + + const textField = wrapper.find('.v-text-field') + const select = wrapper.find('.v-select') + const searchButton = wrapper.find('#search') + expect(textField.exists()).toBe(true) + expect(select.exists()).toBe(true) + expect(searchButton.exists()).toBe(true) + }) +}) diff --git a/frontend/src/views/__tests__/HomeView.spec.ts b/frontend/src/views/__tests__/HomeView.spec.ts index 92e1e214..8a863b5f 100644 --- a/frontend/src/views/__tests__/HomeView.spec.ts +++ b/frontend/src/views/__tests__/HomeView.spec.ts @@ -1,8 +1,107 @@ -import { describe, it } from 'vitest' - +import { describe, expect, it, vi } from 'vitest' import { mount } from '@vue/test-utils' +import { createRouter, createWebHistory } from 'vue-router' +import { routes } from '@/router' +import { createTestingPinia } from '@pinia/testing' +import { useGeneInfoStore } from '@/stores/geneInfo' +import { createVuetify } from 'vuetify' +import * as components from 'vuetify/components' +import * as directives from 'vuetify/directives' import HomeView from '../HomeView.vue' +import { StoreState } from '@/stores/geneInfo' + +const vuetify = createVuetify({ + components, + directives +}) + +const router = createRouter({ + history: createWebHistory(import.meta.env.BASE_URL), + routes: routes +}) +// Mock router push +router.push = vi.fn() + +global.ResizeObserver = require('resize-observer-polyfill') + +describe('HomeView', async () => { + it('renders the header', () => { + const geneData = { + storeState: 'active', + geneSymbol: 'BRCA1', + geneInfo: { + symbol: 'BRCA1', + name: 'Test Gene', + hgncId: '12345', + ensemblId: 'ENSG00000000000001', + entrezId: '12345' + } + } + + const wrapper = mount( + { + template: '' + }, + { + global: { + plugins: [vuetify, router, createTestingPinia({ createSpy: vi.fn() })], + components: { + HomeView + } + } + } + ) + + const store = useGeneInfoStore() + store.storeState = StoreState.Active + store.geneSymbol = geneData.geneSymbol + store.geneInfo = JSON.parse(JSON.stringify(geneData.geneInfo)) + + const logo = wrapper.find('#logo') + const aboutLink = wrapper.find('#about') + const contactLink = wrapper.find('#contact') + expect(logo.exists()).toBe(true) + expect(aboutLink.exists()).toBe(true) + expect(contactLink.exists()).toBe(true) + }) + + it('renders the search bar', () => { + const geneData = { + storeState: 'active', + geneSymbol: 'BRCA1', + geneInfo: { + symbol: 'BRCA1', + name: 'Test Gene', + hgncId: '12345', + ensemblId: 'ENSG00000000000001', + entrezId: '12345' + } + } + + const wrapper = mount( + { + template: '' + }, + { + global: { + plugins: [vuetify, router, createTestingPinia({ createSpy: vi.fn() })], + components: { + HomeView + } + } + } + ) + + const store = useGeneInfoStore() + store.storeState = StoreState.Active + store.geneSymbol = geneData.geneSymbol + store.geneInfo = JSON.parse(JSON.stringify(geneData.geneInfo)) -describe('HomeView.vue', () => { - it.todo('renders the home page') + const textField = wrapper.find('.v-text-field') + const select = wrapper.find('.v-select') + const searchButton = wrapper.find('#search') + expect(textField.exists()).toBe(true) + expect(select.exists()).toBe(true) + expect(searchButton.exists()).toBe(true) + }) }) diff --git a/frontend/vitest.config.ts b/frontend/vitest.config.ts index 17ae9bca..404b305b 100644 --- a/frontend/vitest.config.ts +++ b/frontend/vitest.config.ts @@ -22,7 +22,3 @@ export default mergeConfig( } }) ) -// "vite-plugin-vuetify": "^1.0.2", - -// "vite": "^4.4.9", -// "vitest": "^0.34.3"