diff --git a/frontend/src/api/__tests__/acmgSeqvar.spec.ts b/frontend/src/api/__tests__/acmgSeqvar.spec.ts index 8a8b736c..8518e810 100644 --- a/frontend/src/api/__tests__/acmgSeqvar.spec.ts +++ b/frontend/src/api/__tests__/acmgSeqvar.spec.ts @@ -7,6 +7,7 @@ import { type AcmgRatingBackend } from '@/stores/seqvarAcmgRating' const fetchMocker = createFetchMock(vi) +// Test data const seqVar = new SeqvarImpl('grch37', '1', 123, 'A', 'G') const mockAcmgRating: AcmgRatingBackend = { comment: 'exampleComment', @@ -26,15 +27,19 @@ describe.concurrent('AcmgSeqVar Client', () => { }) it('lists ACMG ratings correctly', async () => { + // arrange: fetchMocker.mockResponse(JSON.stringify([mockAcmgRating])) + // act: const client = new AcmgSeqVarClient() const result = await client.listAcmgRatings() + // assert: expect(result).toEqual([mockAcmgRating]) }) it('fails to list ACMG ratings', async () => { + // arrange: fetchMocker.mockResponse((req) => { if (req.url.includes('acmgSeqvar/list')) { return Promise.resolve(JSON.stringify({ status: 500 })) @@ -42,22 +47,28 @@ describe.concurrent('AcmgSeqVar Client', () => { return Promise.resolve(JSON.stringify({ status: 400 })) }) + // act: const client = new AcmgSeqVarClient() const result = await client.listAcmgRatings() + // assert: expect(result).toEqual({ status: 500 }) }) it('fetches ACMG rating correctly', async () => { + // arrange: fetchMocker.mockResponse(JSON.stringify(mockAcmgRating)) + // act: const client = new AcmgSeqVarClient() const result = await client.fetchAcmgRating(seqVar) + // assert: expect(result).toEqual(mockAcmgRating) }) it('fails to fetch ACMG rating', async () => { + // arrange: fetchMocker.mockResponse((req) => { if (req.url.includes('acmgSeqvar/get')) { return Promise.resolve(JSON.stringify({ status: 500 })) @@ -65,22 +76,28 @@ describe.concurrent('AcmgSeqVar Client', () => { return Promise.resolve(JSON.stringify({ status: 400 })) }) + // act: const client = new AcmgSeqVarClient() const result = await client.fetchAcmgRating(seqVar) + // assert: expect(result).toEqual({ status: 500 }) }) it('saves ACMG rating correctly', async () => { + // arrange: fetchMocker.mockResponse(JSON.stringify(mockAcmgRating)) + // act: const client = new AcmgSeqVarClient() const result = await client.saveAcmgRating(seqVar, mockAcmgRating) + // assert: expect(result).toEqual(mockAcmgRating) }) it('fails to save ACMG rating', async () => { + // arrange: fetchMocker.mockResponse((req) => { if (req.url.includes('acmgSeqvar/create')) { return Promise.resolve(JSON.stringify({ status: 500 })) @@ -88,22 +105,28 @@ describe.concurrent('AcmgSeqVar Client', () => { return Promise.resolve(JSON.stringify({ status: 400 })) }) + // act: const client = new AcmgSeqVarClient() const result = await client.saveAcmgRating(seqVar, mockAcmgRating) + // assert: expect(result).toEqual({ status: 500 }) }) it('updates ACMG rating correctly', async () => { + // arrange: fetchMocker.mockResponse(JSON.stringify(mockAcmgRating)) + // act: const client = new AcmgSeqVarClient() const result = await client.updateAcmgRating(seqVar, mockAcmgRating) + // assert: expect(result).toEqual(mockAcmgRating) }) it('fails to update ACMG rating', async () => { + // arrange: fetchMocker.mockResponse((req) => { if (req.url.includes('acmgSeqvar/update')) { return Promise.resolve(JSON.stringify({ status: 500 })) @@ -111,22 +134,28 @@ describe.concurrent('AcmgSeqVar Client', () => { return Promise.resolve(JSON.stringify({ status: 400 })) }) + // act: const client = new AcmgSeqVarClient() const result = await client.updateAcmgRating(seqVar, mockAcmgRating) + // assert: expect(result).toEqual({ status: 500 }) }) it('deletes ACMG rating correctly', async () => { + // arrange: fetchMocker.mockResponse(JSON.stringify({})) + // act: const client = new AcmgSeqVarClient() const result = await client.deleteAcmgRating(seqVar) + // assert: expect(result).toEqual({}) }) it('fails to delete ACMG rating', async () => { + // arrange: fetchMocker.mockResponse((req) => { if (req.url.includes('acmgSeqvar/delete')) { return Promise.resolve(JSON.stringify({ status: 500 })) @@ -134,9 +163,11 @@ describe.concurrent('AcmgSeqVar Client', () => { return Promise.resolve(JSON.stringify({ status: 400 })) }) + // act: const client = new AcmgSeqVarClient() const result = await client.deleteAcmgRating(seqVar) + // assert: expect(result).toEqual({ status: 500 }) }) }) diff --git a/frontend/src/api/__tests__/annonars.spec.ts b/frontend/src/api/__tests__/annonars.spec.ts index f6b8b3c8..a1f58bba 100644 --- a/frontend/src/api/__tests__/annonars.spec.ts +++ b/frontend/src/api/__tests__/annonars.spec.ts @@ -9,6 +9,7 @@ import { SeqvarImpl } from '@/lib/genomicVars' const fetchMocker = createFetchMock(vi) +// Test data const seqVar = new SeqvarImpl('grch37', '1', 123, 'A', 'G') describe.concurrent('Annonars Client', () => { @@ -18,14 +19,19 @@ describe.concurrent('Annonars Client', () => { }) it('fetches gene info correctly', async () => { + // arrange: fetchMocker.mockResponseOnce(JSON.stringify(BRCA1geneInfo)) + // act: const client = new AnnonarsClient() const result = await client.fetchGeneInfo('BRCA1') + + // assert: expect(JSON.stringify(result)).toEqual(JSON.stringify(BRCA1geneInfo)) }) it('fails to fetch gene info with wrong HGNC id', async () => { + // arrange: fetchMocker.mockResponse((req) => { if (req.url.includes('hgnc_id=BRCA1')) { return Promise.resolve(JSON.stringify(BRCA1geneInfo)) @@ -33,20 +39,28 @@ describe.concurrent('Annonars Client', () => { return Promise.resolve(JSON.stringify({ status: 400 })) }) + // act: const client = new AnnonarsClient() const result = await client.fetchGeneInfo('123') + + // assert: expect(JSON.stringify(result)).toEqual(JSON.stringify({ status: 400 })) }) it('fetches variant info correctly', async () => { + // arrange: fetchMocker.mockResponseOnce(JSON.stringify(BRCA1VariantInfo)) + // act: const client = new AnnonarsClient() const result = await client.fetchVariantInfo(seqVar) + + // assert: expect(JSON.stringify(result)).toEqual(JSON.stringify(BRCA1VariantInfo)) }) it('do removes chr prefix from chromosome if genome release is grch38', async () => { + // arrange: fetchMocker.mockResponse((req) => { if (req.url.includes('chr')) { return Promise.resolve(JSON.stringify(BRCA1VariantInfo)) @@ -54,12 +68,16 @@ describe.concurrent('Annonars Client', () => { return Promise.resolve(JSON.stringify({ status: 400 })) }) + // act: const client = new AnnonarsClient() const result = await client.fetchVariantInfo(seqVar) + + // assert: expect(JSON.stringify(result)).toEqual(JSON.stringify(BRCA1VariantInfo)) }) it('fails to fetch variant info with wrong variant', async () => { + // arrange: const seqVarInvalid = new SeqvarImpl('grch37', '1', 123, 'A', 'T') fetchMocker.mockResponse((req) => { if (req.url.includes('alternative=G')) { @@ -68,20 +86,28 @@ describe.concurrent('Annonars Client', () => { return Promise.resolve(JSON.stringify({ status: 400 })) }) + // act: const client = new AnnonarsClient() const result = await client.fetchVariantInfo(seqVarInvalid) + + // assert: expect(JSON.stringify(result)).toEqual(JSON.stringify({ status: 400 })) }) it('fetches gene clinvar info correctly', async () => { + // arrange: fetchMocker.mockResponseOnce(JSON.stringify(BRCA1geneInfo)) + // act: const client = new AnnonarsClient() const result = await client.fetchGeneClinvarInfo('BRCA1') + + // assert: expect(JSON.stringify(result)).toEqual(JSON.stringify(BRCA1geneInfo)) }) it('fails to fetch gene clinvar info with wrong HGNC id', async () => { + // arrange: fetchMocker.mockResponse((req) => { if (req.url.includes('hgnc_id=BRCA1')) { return Promise.resolve(JSON.stringify(BRCA1geneInfo)) @@ -89,22 +115,30 @@ describe.concurrent('Annonars Client', () => { return Promise.resolve(JSON.stringify({ status: 400 })) }) + // act: const client = new AnnonarsClient() const result = await client.fetchGeneClinvarInfo('123') + + // assert: expect(JSON.stringify(result)).toEqual(JSON.stringify({ status: 400 })) }) it('fetches genes correctly', async () => { + // arrange: fetchMocker.mockResponseOnce(JSON.stringify(EMPSearchInfo)) + // act: const client = new AnnonarsClient() const result = await client.fetchGenes( 'q=BRCA1&fields=hgnc_id,ensembl_gene_id,ncbi_gene_id,symbol' ) + + // assert: expect(JSON.stringify(result)).toEqual(JSON.stringify(EMPSearchInfo)) }) it('fails to fetch genes with wrong query', async () => { + // arrange: fetchMocker.mockResponse((req) => { if (req.url.includes('q=BRCA1')) { return Promise.resolve(JSON.stringify(EMPSearchInfo)) @@ -112,28 +146,39 @@ describe.concurrent('Annonars Client', () => { return Promise.resolve(JSON.stringify({ status: 400 })) }) + // act: const client = new AnnonarsClient() const result = await client.fetchGenes( 'q=BRCA2&fields=hgnc_id,ensembl_gene_id,ncbi_gene_id,symbol' ) + + // assert: expect(JSON.stringify(result)).toEqual(JSON.stringify({ status: 400 })) }) it('fetches gene infos correctly', async () => { + // arrange: fetchMocker.mockResponse(JSON.stringify(BRCA1geneInfo)) + // act: const client = new AnnonarsClient() const result = await client.fetchGeneInfos(['BRCA1', 'BRCA2']) + + // assert: expect(JSON.stringify(result)).toMatch(JSON.stringify(BRCA1geneInfo['genes']['HGNC:1100'])) }) it.fails('fails to fetch gene infos with wrong HGNC id', async () => { + // arrange: fetchMocker.mockResponse(() => { return Promise.resolve(JSON.stringify({ status: 500 })) }) + // act: const client = new AnnonarsClient() const result = await client.fetchGeneInfos(['123', 'BRCA2']) + + // assert: expect(JSON.stringify(result)).toEqual(JSON.stringify({ status: 500 })) }) }) diff --git a/frontend/src/api/__tests__/auth.spec.ts b/frontend/src/api/__tests__/auth.spec.ts index e3c57ddb..1385646c 100644 --- a/frontend/src/api/__tests__/auth.spec.ts +++ b/frontend/src/api/__tests__/auth.spec.ts @@ -12,38 +12,50 @@ describe.concurrent('AuthClient', () => { }) it('should login successfully', async () => { + // arrange: fetchMocker.mockResponseOnce('', { status: 204 }) + // act: const client = new AuthClient() const result = await client.login('testuser', 'password123') + // assert: expect(result).toBe(true) }) it('should fail to login with incorrect credentials', async () => { + // arrange: fetchMocker.mockResponseOnce('', { status: 401 }) + // act: const client = new AuthClient() const result = await client.login('invaliduser', 'invalidpassword') + // assert: expect(result).toBe(false) }) it('should logout successfully', async () => { + // arrange: fetchMocker.mockResponseOnce('Logout Successful', { status: 200 }) + // act: const client = new AuthClient() const result = await client.logout() + // assert: expect(result).toBe('Logout Successful') }) it('should handle logout failure', async () => { + // arrange: fetchMocker.mockResponseOnce('Logout Failed', { status: 500 }) + // act: const client = new AuthClient() const result = await client.logout() + // assert: expect(result).toBe('Logout Failed') }) }) diff --git a/frontend/src/api/__tests__/bookmarks.spec.ts b/frontend/src/api/__tests__/bookmarks.spec.ts index 450135c0..3ecda8d9 100644 --- a/frontend/src/api/__tests__/bookmarks.spec.ts +++ b/frontend/src/api/__tests__/bookmarks.spec.ts @@ -7,6 +7,7 @@ import { BookmarksClient } from '../bookmarks' const fetchMocker = createFetchMock(vi) +// Test data const mockBookmarks: BookmarkData[] = [ { user: '2c0a153e-5e8c-11ee-8c99-0242ac120002', @@ -23,6 +24,7 @@ describe.concurrent('Bookmarks Client', () => { }) it('fetches bookmarks correctly', async () => { + // arrange: fetchMocker.mockResponse((req) => { if (req.url.includes('users/me')) { return Promise.resolve(JSON.stringify({ id: '2c0a153e-5e8c-11ee-8c99-0242ac120002' })) @@ -31,13 +33,17 @@ describe.concurrent('Bookmarks Client', () => { } return Promise.resolve(JSON.stringify({ status: 400 })) }) + + // act: const client = new BookmarksClient() const result = await client.fetchBookmarks() + // assert: expect(result).toEqual(mockBookmarks) }) it('fails to fetch bookmarks', async () => { + // arrange: fetchMocker.mockResponse((req) => { if (req.url.includes('users/me')) { return Promise.resolve(JSON.stringify({ id: '2c0a153e-5e8c-11ee-8c99-0242ac120002' })) @@ -46,63 +52,84 @@ describe.concurrent('Bookmarks Client', () => { } return Promise.resolve(JSON.stringify({ status: 400 })) }) + + // act: const client = new BookmarksClient() const result = await client.fetchBookmarks() + // assert: expect(result).toEqual({ status: 500 }) }) it('fetches bookmark correctly', async () => { + // arrange: fetchMocker.mockResponse(JSON.stringify(mockBookmarks[0])) + // act: const client = new BookmarksClient() const result = await client.fetchBookmark('seqvar', 'HGNC:1100') + // assert: expect(result).toEqual(mockBookmarks[0]) }) it('fails to fetch bookmark', async () => { + // arrange: fetchMocker.mockResponse(JSON.stringify({ detail: 'Internal Server Error' }), { status: 500 }) + // act: const client = new BookmarksClient() const result = await client.fetchBookmark('seqvar', 'HGNC:1100') + // assert: expect(result).toEqual({ detail: 'Internal Server Error' }) }) it('creates bookmark correctly', async () => { + // arrange: fetchMocker.mockResponse(JSON.stringify({})) + // act: const client = new BookmarksClient() const result = await client.createBookmark('seqvar', 'HGNC:1100') + // assert: expect(result).toEqual({}) }) it('fails to create bookmark', async () => { + // arrange: fetchMocker.mockResponse(JSON.stringify({ detail: 'Internal Server Error' }), { status: 500 }) + // act: const client = new BookmarksClient() const result = await client.createBookmark('seqvar', 'HGNC:1100') + // assert: expect(result).toEqual({ detail: 'Internal Server Error' }) }) it('deletes bookmark correctly', async () => { + // arrange: fetchMocker.mockResponse(JSON.stringify({})) + // act: const client = new BookmarksClient() const result = await client.deleteBookmark('seqvar', 'HGNC:1100') + // assert: expect(result).toEqual({}) }) it('fails to delete bookmark', async () => { + // arrange: fetchMocker.mockResponse(JSON.stringify({ detail: 'Internal Server Error' }), { status: 500 }) + // act: const client = new BookmarksClient() const result = await client.deleteBookmark('seqvar', 'HGNC:1100') + // assert: expect(result).toEqual({ detail: 'Internal Server Error' }) }) }) diff --git a/frontend/src/api/__tests__/cadaPrio.spec.ts b/frontend/src/api/__tests__/cadaPrio.spec.ts index bf5de3fe..fa7b6cda 100644 --- a/frontend/src/api/__tests__/cadaPrio.spec.ts +++ b/frontend/src/api/__tests__/cadaPrio.spec.ts @@ -12,22 +12,31 @@ describe.concurrent('Cada Prio Client', () => { }) it('fetches gene impact correctly', async () => { + // arrange: fetchMocker.mockResponseOnce(JSON.stringify({ result: 'pathogenic' })) + // act: const client = new CadaPrioClient() const result = await client.predictGeneImpact(['HP:0000001']) + + // assert: expect(JSON.stringify(result)).toEqual(JSON.stringify({ result: 'pathogenic' })) }) it('fetches gene impact correctly with gene symbols', async () => { + // arrange: fetchMocker.mockResponseOnce(JSON.stringify({ result: 'pathogenic' })) + // act: const client = new CadaPrioClient() const result = await client.predictGeneImpact(['HP:0000001'], ['BRCA1']) + + // assert: expect(JSON.stringify(result)).toEqual(JSON.stringify({ result: 'pathogenic' })) }) it('fails to fetch gene impact with wrong HPO terms', async () => { + // arrange: fetchMocker.mockResponse((req) => { if (req.url.includes('hpo_terms=HP:0000001')) { return Promise.resolve(JSON.stringify({ result: 'pathogenic' })) @@ -35,8 +44,11 @@ describe.concurrent('Cada Prio Client', () => { return Promise.resolve(JSON.stringify({ status: 400 })) }) + // act: const client = new CadaPrioClient() const result = await client.predictGeneImpact(['123']) + + // assert: expect(JSON.stringify(result)).toEqual(JSON.stringify({ status: 400 })) }) }) diff --git a/frontend/src/api/__tests__/caseinfo.spec.ts b/frontend/src/api/__tests__/caseinfo.spec.ts index 5472e6a4..85683648 100644 --- a/frontend/src/api/__tests__/caseinfo.spec.ts +++ b/frontend/src/api/__tests__/caseinfo.spec.ts @@ -6,6 +6,7 @@ import { type Case, Ethnicity, Inheritance, Sex, Zygosity } from '@/stores/case' const fetchMocker = createFetchMock(vi) +// Test data const mockCaseInfo: Case = { pseudonym: '', diseases: [], @@ -26,15 +27,19 @@ describe.concurrent('CaseInfo Client', () => { }) it('fetches case info correctly', async () => { + // arrange: fetchMocker.mockResponse(JSON.stringify(mockCaseInfo)) + // act: const client = new CaseInfoClient() const result = await client.fetchCaseInfo() + // assert: expect(result).toEqual(mockCaseInfo) }) it('fails to fetch case info', async () => { + // arrange: fetchMocker.mockResponse((req) => { if (req.url.includes('caseinfo/get')) { return Promise.resolve(JSON.stringify({ status: 500 })) @@ -42,22 +47,28 @@ describe.concurrent('CaseInfo Client', () => { return Promise.resolve(JSON.stringify({ status: 400 })) }) + // act: const client = new CaseInfoClient() const result = await client.fetchCaseInfo() + // assert: expect(result).toEqual({ status: 500 }) }) it('creates case info correctly', async () => { + // arrange: fetchMocker.mockResponse(JSON.stringify(mockCaseInfo)) + // act: const client = new CaseInfoClient() const result = await client.createCaseInfo(mockCaseInfo) + // assert: expect(result).toEqual(mockCaseInfo) }) it('fails to create case info', async () => { + // arrange: fetchMocker.mockResponse((req) => { if (req.url.includes('caseinfo/create')) { return Promise.resolve(JSON.stringify({ status: 500 })) @@ -65,22 +76,28 @@ describe.concurrent('CaseInfo Client', () => { return Promise.resolve(JSON.stringify({ status: 400 })) }) + // act: const client = new CaseInfoClient() const result = await client.createCaseInfo(mockCaseInfo) + // assert: expect(result).toEqual({ status: 500 }) }) it('updates case info correctly', async () => { + // arrange: fetchMocker.mockResponse(JSON.stringify(mockCaseInfo)) + // act: const client = new CaseInfoClient() const result = await client.updateCaseInfo(mockCaseInfo) + // assert: expect(result).toEqual(mockCaseInfo) }) it('fails to update case info', async () => { + // arrange: fetchMocker.mockResponse((req) => { if (req.url.includes('caseinfo/update')) { return Promise.resolve(JSON.stringify({ status: 500 })) @@ -88,22 +105,28 @@ describe.concurrent('CaseInfo Client', () => { return Promise.resolve(JSON.stringify({ status: 400 })) }) + // act: const client = new CaseInfoClient() const result = await client.updateCaseInfo(mockCaseInfo) + // assert: expect(result).toEqual({ status: 500 }) }) it('deletes case info correctly', async () => { + // arrange: fetchMocker.mockResponse(JSON.stringify({})) + // act: const client = new CaseInfoClient() const result = await client.deleteCaseInfo() + // assert: expect(result).toEqual({}) }) it('fails to delete case info', async () => { + // arrange: fetchMocker.mockResponse((req) => { if (req.url.includes('caseinfo/delete')) { return Promise.resolve(JSON.stringify({ status: 500 })) @@ -111,9 +134,11 @@ describe.concurrent('CaseInfo Client', () => { return Promise.resolve(JSON.stringify({ status: 400 })) }) + // act: const client = new CaseInfoClient() const result = await client.deleteCaseInfo() + // assert: expect(result).toEqual({ status: 500 }) }) }) diff --git a/frontend/src/api/__tests__/common.spec.ts b/frontend/src/api/__tests__/common.spec.ts index 33af0d60..327b3848 100644 --- a/frontend/src/api/__tests__/common.spec.ts +++ b/frontend/src/api/__tests__/common.spec.ts @@ -10,31 +10,56 @@ import { describe.concurrent('API_BASE_PREFIX constants', () => { it('returns the correct API base prefix in production mode', () => { + // arrange: const originalMode = import.meta.env.MODE + + // act: nothing, only test check + + // assert: expect(API_INTERNAL_BASE_PREFIX).toBe('/internal/') import.meta.env.MODE = originalMode }) it('returns the correct API base prefix for annonars in production mode', () => { + // arrange: const originalMode = import.meta.env.MODE + + // act: nothing, only test check + + // assert: expect(API_INTERNAL_BASE_PREFIX_ANNONARS).toBe('/internal/proxy/annonars') import.meta.env.MODE = originalMode }) it('returns the correct API base prefix for mehari in production mode', () => { + // arrange: const originalMode = import.meta.env.MODE + + // act: nothing, only test check + + // assert: expect(API_INTERNAL_BASE_PREFIX_MEHARI).toBe('/internal/proxy/mehari') import.meta.env.MODE = originalMode }) it('returns the correct API base prefix for nginx in production mode', () => { + // arrange: const originalMode = import.meta.env.MODE + + // act: nothing, only test check + + // assert: expect(API_INTERNAL_BASE_PREFIX_NGINX).toBe('/internal/proxy/nginx') import.meta.env.MODE = originalMode }) it('returns the correct API base prefix for cada-prio in production mode', () => { + // arrange: const originalMode = import.meta.env.MODE + + // act: nothing, only test check + + // assert: expect(API_INTERNAL_BASE_PREFIX_CADA_PRIO).toBe('/internal/proxy/cada-prio') import.meta.env.MODE = originalMode }) diff --git a/frontend/src/api/__tests__/dotty.spec.ts b/frontend/src/api/__tests__/dotty.spec.ts index c2692c12..670b300d 100644 --- a/frontend/src/api/__tests__/dotty.spec.ts +++ b/frontend/src/api/__tests__/dotty.spec.ts @@ -12,6 +12,7 @@ describe.concurrent('DottyClient', () => { }) it('should resolve to SPDI successfully', async () => { + // arrange: const mockData: DottyResponse = { success: true, value: { @@ -24,13 +25,16 @@ describe.concurrent('DottyClient', () => { } fetchMocker.mockResponseOnce(JSON.stringify(mockData), { status: 200 }) + // act: const client = new DottyClient() const result = await client.toSpdi('NM_000059.3:c.274G>A') + // assert: expect(result).toEqual(mockData) }) it('should load transcripts successfully', async () => { + // arrange: const mockData = { transcripts: { 'HGNC:1100': { @@ -40,9 +44,11 @@ describe.concurrent('DottyClient', () => { } fetchMocker.mockResponseOnce(JSON.stringify(mockData), { status: 200 }) + // act: const client = new DottyClient() const result = await client.fetchTranscripts('HGNC:1100', 'GRCh37') + // assert: expect(result).toEqual(mockData) }) }) diff --git a/frontend/src/api/__tests__/mehari.spec.ts b/frontend/src/api/__tests__/mehari.spec.ts index 91a162fe..bc8b87c4 100644 --- a/frontend/src/api/__tests__/mehari.spec.ts +++ b/frontend/src/api/__tests__/mehari.spec.ts @@ -9,6 +9,7 @@ import { LinearStrucvarImpl } from '@/lib/genomicVars' const fetchMocker = createFetchMock(vi) +// Test data const seqVar = new SeqvarImpl('grch37', '1', 123, 'A', 'G') const strucVar = new LinearStrucvarImpl('DEL', 'grch37', 'chr17', 43044295, 43044297) @@ -19,22 +20,31 @@ describe.concurrent('Mehari Client', () => { }) it('fetches TxCsq info correctly', async () => { + // arrange: fetchMocker.mockResponseOnce(JSON.stringify(BRCA1TxInfo)) + // act: const client = new MehariClient() const result = await client.retrieveSeqvarsCsq(seqVar, 'HGNC:1100') + + // assert: expect(JSON.stringify(result)).toEqual(JSON.stringify(BRCA1TxInfo)) }) it('fetches TxCsq info correctly without HGNC id', async () => { + // arrange: fetchMocker.mockResponseOnce(JSON.stringify(BRCA1TxInfo)) + // act: const client = new MehariClient() const result = await client.retrieveSeqvarsCsq(seqVar) + + // assert: expect(JSON.stringify(result)).toEqual(JSON.stringify(BRCA1TxInfo)) }) it('fails to fetch variant info with wrong variant', async () => { + // arrange: const seqVarInvalid = new SeqvarImpl('grch37', '1', 123, 'A', 'T') fetchMocker.mockResponse((req) => { if (req.url.includes('alternative=G')) { @@ -43,20 +53,28 @@ describe.concurrent('Mehari Client', () => { return Promise.resolve(JSON.stringify({ status: 400 })) }) + // act: const client = new MehariClient() const result = await client.retrieveSeqvarsCsq(seqVarInvalid) + + // assert: expect(JSON.stringify(result)).toEqual(JSON.stringify({ status: 400 })) }) it('fetches Structur Variant info correctly', async () => { + // arrange: fetchMocker.mockResponseOnce(JSON.stringify(SVInfo)) + // act: const client = new MehariClient() const result = await client.retrieveStrucvarsCsq(strucVar) + + // assert: expect(JSON.stringify(result)).toEqual(JSON.stringify(SVInfo)) }) it('fails to fetch variant info with wrong variant', async () => { + // arrange: const strucVarInvalid = new LinearStrucvarImpl('DUP', 'grch37', 'chr17', 43044295, 43044297) fetchMocker.mockResponse((req) => { if (req.url.includes('DEL')) { @@ -65,8 +83,11 @@ describe.concurrent('Mehari Client', () => { return Promise.resolve(JSON.stringify({ status: 400 })) }) + // act: const client = new MehariClient() const result = await client.retrieveStrucvarsCsq(strucVarInvalid) + + // assert: expect(JSON.stringify(result)).toEqual(JSON.stringify({ status: 400 })) }) }) diff --git a/frontend/src/api/__tests__/misc.spec.ts b/frontend/src/api/__tests__/misc.spec.ts index 48c95517..45e97193 100644 --- a/frontend/src/api/__tests__/misc.spec.ts +++ b/frontend/src/api/__tests__/misc.spec.ts @@ -12,10 +12,14 @@ describe.concurrent('Misc Client', () => { }) it('fetches version info correctly', async () => { + // arrange: fetchMocker.mockResponseOnce('v0.0.0') + // act: const client = new MiscClient() const result = await client.fetchVersion() + + // assert: expect(result).toEqual('v0.0.0') }) }) diff --git a/frontend/src/api/__tests__/settings.spec.ts b/frontend/src/api/__tests__/settings.spec.ts index 7af17775..bc58056f 100644 --- a/frontend/src/api/__tests__/settings.spec.ts +++ b/frontend/src/api/__tests__/settings.spec.ts @@ -12,52 +12,76 @@ describe.concurrent('Settings Client', () => { }) it('fetches version info correctly', async () => { + // arrange: fetchMocker.mockResponseOnce( JSON.stringify({ matomo_host: 'https://matomo.example.com/', matomo_site_id: '1' }) ) + // act: const client = new SettingsClient() const result = await client.fetchFrontendSettings() + + // assert: expect(result).toEqual({ matomo_host: 'https://matomo.example.com/', matomo_site_id: '1' }) }) it('handles different settings data', async () => { + // arrange: const alternativeSettings = { theme: 'dark', language: 'en' } fetchMocker.mockResponseOnce(JSON.stringify(alternativeSettings)) + // act: const client = new SettingsClient() const result = await client.fetchFrontendSettings() + + // assert: expect(result).toEqual(alternativeSettings) }) it('handles empty settings response', async () => { + // arrange: fetchMocker.mockResponseOnce(JSON.stringify({})) + // act: const client = new SettingsClient() const result = await client.fetchFrontendSettings() + + // assert: expect(result).toEqual({}) }) it('handles null settings response', async () => { + // arrange: fetchMocker.mockResponseOnce('null') + // act: const client = new SettingsClient() const result = await client.fetchFrontendSettings() + + // assert: expect(result).toBeNull() }) it('handles server error when fetching settings', async () => { + // arrange: const errorMessage = 'Internal Server Error' // The statusText for a 500 error fetchMocker.mockResponseOnce(JSON.stringify({ msg: 'Internal server error' }), { status: 500 }) + // act: const client = new SettingsClient() + + // assert: await expect(client.fetchFrontendSettings()).rejects.toThrow(errorMessage) }) it('handles network error when fetching settings', async () => { + // arrange: fetchMocker.mockReject(new Error('Network Error')) + // act: const client = new SettingsClient() + + // assert: await expect(client.fetchFrontendSettings()).rejects.toThrow('Network Error') }) }) diff --git a/frontend/src/api/__tests__/users.spec.ts b/frontend/src/api/__tests__/users.spec.ts index 69e1607c..47491f40 100644 --- a/frontend/src/api/__tests__/users.spec.ts +++ b/frontend/src/api/__tests__/users.spec.ts @@ -12,6 +12,7 @@ describe.concurrent('Users Client', () => { }) it('should fetch current user profile successfully', async () => { + // arrange: const userData = { id: '1', email: 'test@example.com', @@ -21,17 +22,22 @@ describe.concurrent('Users Client', () => { } fetchMocker.mockResponseOnce(JSON.stringify(userData), { status: 200 }) + // act: const client = new UsersClient() const result = await client.fetchCurrentUserProfile() + // assert: expect(result).toEqual(userData) }) it('should throw UnauthenticatedError when not authenticated', async () => { + // arrange: fetchMocker.mockResponseOnce('', { status: 401 }) + // act: const client = new UsersClient() + // assert: try { await client.fetchCurrentUserProfile() expect(true).toBe(false) @@ -41,10 +47,13 @@ describe.concurrent('Users Client', () => { }) it('should throw UnauthenticatedError on unexpected status', async () => { + // arrange: fetchMocker.mockResponseOnce('', { status: 500 }) + // act: const client = new UsersClient() + // assert: try { await client.fetchCurrentUserProfile() expect(true).toBe(false) diff --git a/frontend/src/api/__tests__/utils.spec.ts b/frontend/src/api/__tests__/utils.spec.ts index 85356deb..7e9c8c2d 100644 --- a/frontend/src/api/__tests__/utils.spec.ts +++ b/frontend/src/api/__tests__/utils.spec.ts @@ -14,50 +14,45 @@ describe.concurrent('Utils Client', () => { }) it('sends a test email correctly', async () => { - // Prepare the mock response + // arrange: const mockResponse = { msg: 'Test email sent successfully' } fetchMocker.mockResponseOnce(JSON.stringify(mockResponse)) - // Instantiate the client and perform the action + // act: const client = new UtilsClient() const email_to = 'test@example.com' const result = await client.sendTestEmail(email_to) - // Assert that the fetch was called correctly + // assert: expect(fetchMocker).toHaveBeenCalledWith( `${API_V1_BASE_PREFIX}utils/test-email/?email_to=${email_to}`, { method: 'POST' } ) - - // Assert the response matches the expected result expect(result).toEqual(mockResponse) }) it('handles invalid email address error', async () => { - // Prepare the mock response for an invalid email address + // arrange: const mockErrorResponse = { msg: 'Invalid email address' } fetchMocker.mockResponseOnce(JSON.stringify(mockErrorResponse), { status: 400 }) + // act: const client = new UtilsClient() const invalidEmail = 'invalid-email' - // Try-catch block to handle the expected error + // assert: try { await client.sendTestEmail(invalidEmail) - // If no error is thrown, force the test to fail expect(true).toBe(false) } catch (error) { - // Assert the fetch was called with the invalid email expect(fetchMocker).toHaveBeenCalledWith( expect.stringContaining(`email_to=${invalidEmail}`), { method: 'POST' } ) - - // Assert the error message matches the expected result expect(error).toEqual( new AssertionError({ message: 'expected true to be false // Object.is equality' }) ) @@ -65,25 +60,22 @@ describe.concurrent('Utils Client', () => { }) it('handles server error when sending an email', async () => { - // Prepare the mock response for a server error + // arrange: const mockServerErrorResponse = { msg: 'Internal server error' } fetchMocker.mockResponseOnce(JSON.stringify(mockServerErrorResponse), { status: 500 }) + // act: const client = new UtilsClient() const email_to = 'test@example.com' - // Try-catch block to handle the expected error + // assert: try { await client.sendTestEmail(email_to) - // If no error is thrown, force the test to fail expect(true).toBe(false) } catch (error) { - // Assert the fetch was called correctly expect(fetchMocker).toHaveBeenCalledWith(expect.stringContaining(`email_to=${email_to}`), { method: 'POST' }) - - // Assert the error message matches the expected server error expect(error).toEqual( new AssertionError({ message: 'expected true to be false // Object.is equality' }) ) diff --git a/frontend/src/api/__tests__/viguno.spec.ts b/frontend/src/api/__tests__/viguno.spec.ts index b8c9eaab..b023816f 100644 --- a/frontend/src/api/__tests__/viguno.spec.ts +++ b/frontend/src/api/__tests__/viguno.spec.ts @@ -12,136 +12,200 @@ describe.concurrent('Viguno Client', () => { }) it('resolves OMIM term by ID correctly', async () => { + // arrange: const mockOmimTerm = { id: 'OMIM:123456', name: 'Example Disease' } fetchMocker.mockResponseOnce(JSON.stringify(mockOmimTerm)) + // act: const client = new VigunoClient() const result = await client.resolveOmimTermById('123456') + + // assert: expect(result).toEqual(mockOmimTerm) }) it('handles non-existent OMIM term ID', async () => { + // arrange: const errorMessage = 'OMIM term not found' fetchMocker.mockResponseOnce(JSON.stringify({ msg: errorMessage }), { status: 404 }) + // act: const client = new VigunoClient() + + // assert: await expect(client.resolveOmimTermById('999999')).rejects.toThrow(errorMessage) }) it('handles server error when resolving OMIM term by ID', async () => { + // arrange: const errorMessage = 'Internal Server Error' fetchMocker.mockResponseOnce(JSON.stringify({ msg: errorMessage }), { status: 500 }) + // act: const client = new VigunoClient() + + // assert: await expect(client.resolveOmimTermById('123456')).rejects.toThrow(errorMessage) }) it('handles network error when resolving OMIM term by ID', async () => { + // arrange: fetchMocker.mockReject(new Error('Network Error')) + // act: const client = new VigunoClient() + + // assert: await expect(client.resolveOmimTermById('123456')).rejects.toThrow('Network Error') }) it('returns a list of OMIM terms for a valid query', async () => { + // arrange: const mockResponse = [ { id: 'OMIM:123456', name: 'Example Disease 1' }, { id: 'OMIM:234567', name: 'Example Disease 2' } ] fetchMocker.mockResponseOnce(JSON.stringify(mockResponse)) + // act: const client = new VigunoClient() const result = await client.queryOmimTermsByName('Example') + + // assert: expect(result).toEqual(mockResponse) }) it('returns an empty list for a query with no results', async () => { + // arrange: fetchMocker.mockResponseOnce(JSON.stringify([])) + // act: const client = new VigunoClient() const result = await client.queryOmimTermsByName('NonExistentDisease') + + // assert: expect(result).toEqual([]) }) it('handles server error for a query', async () => { + // arrange: const errorMessage = 'Internal Server Error' fetchMocker.mockResponseOnce(JSON.stringify({ msg: errorMessage }), { status: 500 }) + // act: const client = new VigunoClient() + + // assert: await expect(client.queryOmimTermsByName('Example')).rejects.toThrow(errorMessage) }) it('handles network error during a query', async () => { + // arrange: fetchMocker.mockReject(new Error('Network Error')) + // act: const client = new VigunoClient() + + // assert: await expect(client.queryOmimTermsByName('Example')).rejects.toThrow('Network Error') }) it('resolves HPO term by ID correctly', async () => { + // arrange: const mockHpoTerm = { id: 'HP:0000118', name: 'Phenotypic abnormality' } fetchMocker.mockResponseOnce(JSON.stringify(mockHpoTerm)) + // act: const client = new VigunoClient() const result = await client.resolveHpoTermById('0000118') + + // assert: expect(result).toEqual(mockHpoTerm) }) it('handles non-existent HPO term ID', async () => { + // arrange: const errorMessage = 'HPO term not found' fetchMocker.mockResponseOnce(JSON.stringify({ msg: errorMessage }), { status: 404 }) + // act: const client = new VigunoClient() + + // assert: await expect(client.resolveHpoTermById('9999999')).rejects.toThrow(errorMessage) }) it('handles server error when resolving HPO term by ID', async () => { + // arrange: const errorMessage = 'Internal Server Error' fetchMocker.mockResponseOnce(JSON.stringify({ msg: errorMessage }), { status: 500 }) + // act: const client = new VigunoClient() + + // assert: await expect(client.resolveHpoTermById('0000118')).rejects.toThrow(errorMessage) }) it('handles network error when resolving HPO term by ID', async () => { + // arrange: fetchMocker.mockReject(new Error('Network Error')) + // act: const client = new VigunoClient() + + // assert: await expect(client.resolveHpoTermById('0000118')).rejects.toThrow('Network Error') }) it('queries HPO terms by name correctly', async () => { + // arrange: const mockHpoTerms = [ { id: 'HP:0000118', name: 'Phenotypic abnormality' }, { id: 'HP:0000152', name: 'Abnormality of head or neck' } ] fetchMocker.mockResponseOnce(JSON.stringify(mockHpoTerms)) + // act: const client = new VigunoClient() const result = await client.queryHpoTermsByName('Phenotypic') + + // assert: expect(result).toEqual(mockHpoTerms) }) it('returns an empty list for a name query with no results', async () => { + // arrange: fetchMocker.mockResponseOnce(JSON.stringify([])) + // act: const client = new VigunoClient() const result = await client.queryHpoTermsByName('NonExistentTerm') + + // assert: expect(result).toEqual([]) }) it('handles server error during name query', async () => { + // arrange: const errorMessage = 'Internal Server Error' fetchMocker.mockResponseOnce(JSON.stringify({ msg: errorMessage }), { status: 500 }) + // act: const client = new VigunoClient() + + // assert: await expect(client.queryHpoTermsByName('Phenotypic')).rejects.toThrow(errorMessage) }) it('handles network error during name query', async () => { + // arrange: fetchMocker.mockReject(new Error('Network Error')) + // act: const client = new VigunoClient() + + // assert: await expect(client.queryHpoTermsByName('Phenotypic')).rejects.toThrow('Network Error') }) }) diff --git a/frontend/src/lib/__tests__/acmgSeqvar.spec.ts b/frontend/src/lib/__tests__/acmgSeqvar.spec.ts index b6694c50..c64c3acd 100644 --- a/frontend/src/lib/__tests__/acmgSeqvar.spec.ts +++ b/frontend/src/lib/__tests__/acmgSeqvar.spec.ts @@ -10,7 +10,12 @@ import { describe.concurrent('MultiSourceAcmgCriteriaState', () => { it('should have correct default values', () => { + // arrange: nothing, only test check + + // act: const AcmgCriteriaState = new MultiSourceAcmgCriteriaState() + + // assert: // Check keys of AcmgCriteriaState.criteriaStates expect(Object.keys(AcmgCriteriaState.criteriaStates).length).toEqual(4) expect(AcmgCriteriaState.criteriaStates).toHaveProperty(StateSource.Default) @@ -36,119 +41,174 @@ describe.concurrent('MultiSourceAcmgCriteriaState', () => { }) it('should correctly get criteria state', () => { + // arrange: nothing, only test check + + // act: const AcmgCriteriaState = new MultiSourceAcmgCriteriaState() + + // assert: expect(AcmgCriteriaState.getCriteriaState(AcmgCriteria.PVS1)).toEqual( AcmgCriteriaState.criteriaStates[StateSource.Default][AcmgCriteria.PVS1] ) }) it('should correctly get criteria state from interVar', () => { - const AcmgCriteriaState = new MultiSourceAcmgCriteriaState() + // arrange: const criteriaState = { criteria: AcmgCriteria.PVS1, presence: Presence.Present, evidenceLevel: AcmgEvidenceLevel.PathogenicVeryStrong } + + // act: + const AcmgCriteriaState = new MultiSourceAcmgCriteriaState() AcmgCriteriaState.setPresence(StateSource.InterVar, AcmgCriteria.PVS1, Presence.Present) + + // assert: expect(AcmgCriteriaState.getCriteriaState(AcmgCriteria.PVS1)).toStrictEqual(criteriaState) }) it('should correctly get criteria state from server', () => { - const AcmgCriteriaState = new MultiSourceAcmgCriteriaState() + // arrange: const criteriaState = { criteria: AcmgCriteria.PVS1, presence: Presence.Present, evidenceLevel: AcmgEvidenceLevel.PathogenicVeryStrong } + + // act: + const AcmgCriteriaState = new MultiSourceAcmgCriteriaState() AcmgCriteriaState.setPresence(StateSource.Server, AcmgCriteria.PVS1, Presence.Present) + + // assert: expect(AcmgCriteriaState.getCriteriaState(AcmgCriteria.PVS1)).toStrictEqual(criteriaState) }) it('should correctly get criteria state from server and InterVar', () => { - const AcmgCriteriaState = new MultiSourceAcmgCriteriaState() + // arrange: const criteriaState = { criteria: AcmgCriteria.PVS1, presence: Presence.Absent, evidenceLevel: AcmgEvidenceLevel.PathogenicVeryStrong } + + // act: + const AcmgCriteriaState = new MultiSourceAcmgCriteriaState() AcmgCriteriaState.setPresence(StateSource.InterVar, AcmgCriteria.PVS1, Presence.Present) AcmgCriteriaState.setPresence(StateSource.Server, AcmgCriteria.PVS1, Presence.Absent) + + // assert: expect(AcmgCriteriaState.getCriteriaState(AcmgCriteria.PVS1)).toStrictEqual(criteriaState) }) it('should correctly get criteria state with invalid request', () => { - const AcmgCriteriaState = new MultiSourceAcmgCriteriaState() + // arrange: const criteriaState = { criteria: 'invalid' as AcmgCriteria, presence: Presence.Unknown, evidenceLevel: AcmgEvidenceLevel.NotSet } + + // act: + const AcmgCriteriaState = new MultiSourceAcmgCriteriaState() + + // assert: expect(AcmgCriteriaState.getCriteriaState('invalid' as AcmgCriteria)).toStrictEqual( criteriaState ) }) it('should correctly get criteria state from default source', () => { - const AcmgCriteriaState = new MultiSourceAcmgCriteriaState() + // arrange: const criteriaState = { criteria: AcmgCriteria.PVS1, presence: Presence.Unknown, evidenceLevel: AcmgEvidenceLevel.PathogenicVeryStrong } + + // act: + const AcmgCriteriaState = new MultiSourceAcmgCriteriaState() + + // assert: expect( AcmgCriteriaState.getCriteriaStateFromSource(AcmgCriteria.PVS1, StateSource.Default) ).toStrictEqual(criteriaState) }) it('should correctly get criteria state from interVar source', () => { - const AcmgCriteriaState = new MultiSourceAcmgCriteriaState() + // arrange: const criteriaState = { criteria: AcmgCriteria.PVS1, presence: Presence.Present, evidenceLevel: AcmgEvidenceLevel.NotSet } + + // act: + const AcmgCriteriaState = new MultiSourceAcmgCriteriaState() AcmgCriteriaState.setPresence(StateSource.InterVar, AcmgCriteria.PVS1, Presence.Present) + + // assert: expect( AcmgCriteriaState.getCriteriaStateFromSource(AcmgCriteria.PVS1, StateSource.InterVar) ).toStrictEqual(criteriaState) }) it('should correctly get criteria state from server source', () => { - const AcmgCriteriaState = new MultiSourceAcmgCriteriaState() + // arrange: const criteriaState = { criteria: AcmgCriteria.PVS1, presence: Presence.Present, evidenceLevel: AcmgEvidenceLevel.NotSet } + + // act: + const AcmgCriteriaState = new MultiSourceAcmgCriteriaState() AcmgCriteriaState.setPresence(StateSource.Server, AcmgCriteria.PVS1, Presence.Present) + + // assert: expect( AcmgCriteriaState.getCriteriaStateFromSource(AcmgCriteria.PVS1, StateSource.Server) ).toStrictEqual(criteriaState) }) it('should correctly get criteria state from user source', () => { - const AcmgCriteriaState = new MultiSourceAcmgCriteriaState() + // arrange: const criteriaState = { criteria: AcmgCriteria.PVS1, presence: Presence.Present, evidenceLevel: AcmgEvidenceLevel.NotSet } + + // act: + const AcmgCriteriaState = new MultiSourceAcmgCriteriaState() AcmgCriteriaState.setPresence(StateSource.User, AcmgCriteria.PVS1, Presence.Present) + + // assert: expect( AcmgCriteriaState.getCriteriaStateFromSource(AcmgCriteria.PVS1, StateSource.User) ).toStrictEqual(criteriaState) }) it('should throw error if getting invalid criteria from source', () => { + // arrange: nothing, only test check + + // act: const AcmgCriteriaState = new MultiSourceAcmgCriteriaState() + + // assert: expect(() => AcmgCriteriaState.getCriteriaStateFromSource('invalid' as AcmgCriteria, StateSource.User) ).toThrowError() }) it('should correctly set presence for InterVar', () => { + // arrange: nothing, only test check + + // act: const AcmgCriteriaState = new MultiSourceAcmgCriteriaState() AcmgCriteriaState.setPresence(StateSource.InterVar, AcmgCriteria.PVS1, Presence.Present) + + // assert: expect( AcmgCriteriaState.criteriaStates[StateSource.InterVar][AcmgCriteria.PVS1].presence ).toEqual(Presence.Present) @@ -162,8 +222,13 @@ describe.concurrent('MultiSourceAcmgCriteriaState', () => { }) it('should correctly set presence for Server', () => { + // arrange: nothing, only test check + + // act: const AcmgCriteriaState = new MultiSourceAcmgCriteriaState() AcmgCriteriaState.setPresence(StateSource.Server, AcmgCriteria.PVS1, Presence.Present) + + // assert: expect( AcmgCriteriaState.criteriaStates[StateSource.Server][AcmgCriteria.PVS1].presence ).toEqual(Presence.Present) @@ -177,8 +242,13 @@ describe.concurrent('MultiSourceAcmgCriteriaState', () => { }) it('should correctly set presence for User', () => { + // arrange: nothing, only test check + + // act: const AcmgCriteriaState = new MultiSourceAcmgCriteriaState() AcmgCriteriaState.setPresence(StateSource.User, AcmgCriteria.PVS1, Presence.Present) + + // assert: expect(AcmgCriteriaState.criteriaStates[StateSource.User][AcmgCriteria.PVS1].presence).toEqual( Presence.Present ) @@ -192,13 +262,21 @@ describe.concurrent('MultiSourceAcmgCriteriaState', () => { }) it('should raise error for setting present for Default', () => { + // arrange: nothing, only test check + + // act: const AcmgCriteriaState = new MultiSourceAcmgCriteriaState() + + // assert: expect(() => AcmgCriteriaState.setPresence(StateSource.Default, AcmgCriteria.PVS1, Presence.Present) ).toThrowError() }) it('should correctly set absent presence for User', () => { + // arrange: nothing, only test check + + // act: const AcmgCriteriaState = new MultiSourceAcmgCriteriaState() AcmgCriteriaState.setPresence(StateSource.User, AcmgCriteria.PVS1, Presence.Absent) expect(AcmgCriteriaState.criteriaStates[StateSource.User][AcmgCriteria.PVS1].presence).toEqual( @@ -206,12 +284,17 @@ describe.concurrent('MultiSourceAcmgCriteriaState', () => { ) // Set presence to absent AcmgCriteriaState.setUserPresenceAbsent() + + // assert: expect(AcmgCriteriaState.criteriaStates[StateSource.User][AcmgCriteria.PVS1].presence).toEqual( Presence.Absent ) }) it('should correctly set interVar presence for User', () => { + // arrange: nothing, only test check + + // act: const AcmgCriteriaState = new MultiSourceAcmgCriteriaState() AcmgCriteriaState.setPresence(StateSource.User, AcmgCriteria.PVS1, Presence.Absent) AcmgCriteriaState.setPresence(StateSource.InterVar, AcmgCriteria.PVS1, Presence.Present) @@ -220,12 +303,17 @@ describe.concurrent('MultiSourceAcmgCriteriaState', () => { ) // Set presence to unknown AcmgCriteriaState.setUserPresenceInterVar() + + // assert: expect(AcmgCriteriaState.criteriaStates[StateSource.User][AcmgCriteria.PVS1].presence).toEqual( Presence.Present ) }) it('should correctly set server presence for User', () => { + // arrange: nothing, only test check + + // act: const AcmgCriteriaState = new MultiSourceAcmgCriteriaState() AcmgCriteriaState.setPresence(StateSource.User, AcmgCriteria.PVS1, Presence.Absent) AcmgCriteriaState.setPresence(StateSource.Server, AcmgCriteria.PVS1, Presence.Present) @@ -234,18 +322,25 @@ describe.concurrent('MultiSourceAcmgCriteriaState', () => { ) // Set presence to unknown AcmgCriteriaState.setUserPresenceServer() + + // assert: expect(AcmgCriteriaState.criteriaStates[StateSource.User][AcmgCriteria.PVS1].presence).toEqual( Presence.Present ) }) it('should correctly set evidence level for user', () => { + // arrange: nothing, only test check + + // act: const AcmgCriteriaState = new MultiSourceAcmgCriteriaState() AcmgCriteriaState.setEvidenceLevel( StateSource.User, AcmgCriteria.PVS1, AcmgEvidenceLevel.PathogenicModerate ) + + // assert: expect( AcmgCriteriaState.criteriaStates[StateSource.User][AcmgCriteria.PVS1].evidenceLevel ).toEqual(AcmgEvidenceLevel.PathogenicModerate) @@ -259,12 +354,17 @@ describe.concurrent('MultiSourceAcmgCriteriaState', () => { }) it('should correctly set evidence level for server', () => { + // arrange: nothing, only test check + + // act: const AcmgCriteriaState = new MultiSourceAcmgCriteriaState() AcmgCriteriaState.setEvidenceLevel( StateSource.Server, AcmgCriteria.PVS1, AcmgEvidenceLevel.PathogenicModerate ) + + // assert: expect( AcmgCriteriaState.criteriaStates[StateSource.Server][AcmgCriteria.PVS1].evidenceLevel ).toEqual(AcmgEvidenceLevel.PathogenicModerate) @@ -278,12 +378,17 @@ describe.concurrent('MultiSourceAcmgCriteriaState', () => { }) it('should correctly set evidence level for interVar', () => { + // arrange: nothing, only test check + + // act: const AcmgCriteriaState = new MultiSourceAcmgCriteriaState() AcmgCriteriaState.setEvidenceLevel( StateSource.InterVar, AcmgCriteria.PVS1, AcmgEvidenceLevel.PathogenicModerate ) + + // assert: expect( AcmgCriteriaState.criteriaStates[StateSource.InterVar][AcmgCriteria.PVS1].evidenceLevel ).toEqual(AcmgEvidenceLevel.PathogenicModerate) @@ -297,7 +402,12 @@ describe.concurrent('MultiSourceAcmgCriteriaState', () => { }) it('should raise error for setting evidence level for Default', () => { + // arrange: nothing, only test check + + // act: const AcmgCriteriaState = new MultiSourceAcmgCriteriaState() + + // assert: expect(() => AcmgCriteriaState.setEvidenceLevel( StateSource.Default, @@ -308,9 +418,13 @@ describe.concurrent('MultiSourceAcmgCriteriaState', () => { }) it('should correctly get States', () => { - const AcmgCriteriaState = new MultiSourceAcmgCriteriaState() + // arrange: nothing, only test check + // act: + const AcmgCriteriaState = new MultiSourceAcmgCriteriaState() const criteriaStates = AcmgCriteriaState.getStates() + + // assert: // Check keys of AcmgCriteriaState.criteriaStates expect(Object.keys(criteriaStates).length).toEqual(4) expect(criteriaStates).toHaveProperty(StateSource.Default) @@ -336,6 +450,9 @@ describe.concurrent('MultiSourceAcmgCriteriaState', () => { }) it('should correctly get evidence counts', () => { + // arrange: nothing, only test check + + // act: const AcmgCriteriaState = new MultiSourceAcmgCriteriaState() AcmgCriteriaState.setPresence(StateSource.InterVar, AcmgCriteria.PVS1, Presence.Present) AcmgCriteriaState.setPresence(StateSource.InterVar, AcmgCriteria.PS1, Presence.Present) @@ -349,6 +466,7 @@ describe.concurrent('MultiSourceAcmgCriteriaState', () => { AcmgCriteriaState.setPresence(StateSource.InterVar, AcmgCriteria.BS2, Presence.Present) AcmgCriteriaState.setPresence(StateSource.InterVar, AcmgCriteria.BS3, Presence.Present) + // assert: expect( AcmgCriteriaState.getActiveEvidenceCounts(AcmgEvidenceLevel.PathogenicVeryStrong) ).toEqual(1) @@ -470,6 +588,9 @@ describe.concurrent( `should return 'Pathogenic' for 'PVS1: %s, PS1: %s, PS2: %s, PM1: %s, PM2: %s, PM3: %s, PM4: %s, PP1: %s, PP2: %s, PP3: %s, PP4: %s' with no confclicts`, (pvs1, ps1, ps2, pm1, pm2, pm3, pp1, pp2, pp3, pp4) => { + // arrange: nothing, only test check + + // act: const AcmgCriteriaState = new MultiSourceAcmgCriteriaState() AcmgCriteriaState.setPresence(StateSource.InterVar, AcmgCriteria.PVS1, pvs1) AcmgCriteriaState.setPresence(StateSource.InterVar, AcmgCriteria.PS1, ps1) @@ -481,6 +602,8 @@ describe.concurrent( AcmgCriteriaState.setPresence(StateSource.InterVar, AcmgCriteria.PP2, pp2) AcmgCriteriaState.setPresence(StateSource.InterVar, AcmgCriteria.PP3, pp3) AcmgCriteriaState.setPresence(StateSource.InterVar, AcmgCriteria.PP4, pp4) + + // assert: expect(AcmgCriteriaState.getAcmgClass()).toEqual(['Pathogenic', false]) } ) @@ -574,6 +697,9 @@ describe.concurrent( `should return 'Likely pathogenic' for 'PVS1: %s, PS1: %s, PS2: %s, PM1: %s, PM2: %s, PM3: %s, PM4: %s, PP1: %s, PP2: %s, PP3: %s, PP4: %s' with no confclicts`, (pvs1, ps1, ps2, pm1, pm2, pm3, pp1, pp2, pp3, pp4) => { + // arrange: nothing, only test check + + // act: const AcmgCriteriaState = new MultiSourceAcmgCriteriaState() AcmgCriteriaState.setPresence(StateSource.InterVar, AcmgCriteria.PVS1, pvs1) AcmgCriteriaState.setPresence(StateSource.InterVar, AcmgCriteria.PS1, ps1) @@ -585,6 +711,8 @@ describe.concurrent( AcmgCriteriaState.setPresence(StateSource.InterVar, AcmgCriteria.PP2, pp2) AcmgCriteriaState.setPresence(StateSource.InterVar, AcmgCriteria.PP3, pp3) AcmgCriteriaState.setPresence(StateSource.InterVar, AcmgCriteria.PP4, pp4) + + // assert: expect(AcmgCriteriaState.getAcmgClass()).toEqual(['Likely pathogenic', false]) } ) @@ -598,12 +726,17 @@ describe.concurrent( ])( `should return 'Benign' for 'BA1: %s, BS1: %s, BS2: %s, BP1: %s, BP2: %s' with no confclicts`, (ba1, bs1, bs2, bp1, bp2) => { + // arrange: nothing, only test check + + // act: const AcmgCriteriaState = new MultiSourceAcmgCriteriaState() AcmgCriteriaState.setPresence(StateSource.InterVar, AcmgCriteria.BA1, ba1) AcmgCriteriaState.setPresence(StateSource.InterVar, AcmgCriteria.BS1, bs1) AcmgCriteriaState.setPresence(StateSource.InterVar, AcmgCriteria.BS2, bs2) AcmgCriteriaState.setPresence(StateSource.InterVar, AcmgCriteria.BP1, bp1) AcmgCriteriaState.setPresence(StateSource.InterVar, AcmgCriteria.BP2, bp2) + + // assert: expect(AcmgCriteriaState.getAcmgClass()).toEqual(['Benign', false]) } ) @@ -615,12 +748,17 @@ describe.concurrent( `should return 'Likely benign' for 'BA1: %s, BS1: %s, BS2: %s, BP1: %s, BP2: %s' with no confclicts`, (ba1, bs1, bs2, bp1, bp2) => { + // arrange: nothing, only test check + + // act: const AcmgCriteriaState = new MultiSourceAcmgCriteriaState() AcmgCriteriaState.setPresence(StateSource.InterVar, AcmgCriteria.BA1, ba1) AcmgCriteriaState.setPresence(StateSource.InterVar, AcmgCriteria.BS1, bs1) AcmgCriteriaState.setPresence(StateSource.InterVar, AcmgCriteria.BS2, bs2) AcmgCriteriaState.setPresence(StateSource.InterVar, AcmgCriteria.BP1, bp1) AcmgCriteriaState.setPresence(StateSource.InterVar, AcmgCriteria.BP2, bp2) + + // assert: expect(AcmgCriteriaState.getAcmgClass()).toEqual(['Likely benign', false]) } ) @@ -638,12 +776,17 @@ describe.concurrent( `should return 'Uncertain significance' for 'PM1: %s, PP1: %s, PP2: %s, BS1: %s, BP1: %s' with no confclicts`, (pm1, pp1, pp2, bs1, bp1) => { + // arrange: nothing, only test check + + // act: const AcmgCriteriaState = new MultiSourceAcmgCriteriaState() AcmgCriteriaState.setPresence(StateSource.InterVar, AcmgCriteria.PM1, pm1) AcmgCriteriaState.setPresence(StateSource.InterVar, AcmgCriteria.PP1, pp1) AcmgCriteriaState.setPresence(StateSource.InterVar, AcmgCriteria.PP2, pp2) AcmgCriteriaState.setPresence(StateSource.InterVar, AcmgCriteria.BS1, bs1) AcmgCriteriaState.setPresence(StateSource.InterVar, AcmgCriteria.BP1, bp1) + + // assert: expect(AcmgCriteriaState.getAcmgClass()).toEqual(['Uncertain significance', false]) } ) @@ -656,12 +799,17 @@ describe.concurrent( `should return 'Conflicting' for 'PVS1: %s, PS1: %s, PS2: %s, BA1: %s, BS1: %s' with confclicts`, (pvs1, ps1, ps2, ba1, bs1) => { + // arrange: nothing, only test check + + // act: const AcmgCriteriaState = new MultiSourceAcmgCriteriaState() AcmgCriteriaState.setPresence(StateSource.InterVar, AcmgCriteria.PVS1, pvs1) AcmgCriteriaState.setPresence(StateSource.InterVar, AcmgCriteria.PS1, ps1) AcmgCriteriaState.setPresence(StateSource.InterVar, AcmgCriteria.PS2, ps2) AcmgCriteriaState.setPresence(StateSource.InterVar, AcmgCriteria.BA1, ba1) AcmgCriteriaState.setPresence(StateSource.InterVar, AcmgCriteria.BS1, bs1) + + // assert: expect(AcmgCriteriaState.getAcmgClass()).toEqual(['Conflicting', true]) } ) @@ -672,6 +820,9 @@ describe.concurrent( 'MultiSourceAcmgCriteriaState ACMG class computation with user override', () => { it(`should return 'Likely pathogenic' for PVS1 as Moderate and PS1`, () => { + // arrange: nothing, only test check + + // act: const AcmgCriteriaState = new MultiSourceAcmgCriteriaState() // Set PVS1 and PS1 to present AcmgCriteriaState.setPresence(StateSource.User, AcmgCriteria.PVS1, Presence.Present) @@ -685,11 +836,15 @@ describe.concurrent( AcmgEvidenceLevel.PathogenicModerate ) + // assert: // Expect to return 'Likely pathogenic' expect(AcmgCriteriaState.getAcmgClass()).toEqual(['Likely pathogenic', false]) }) it(`should return 'Likely benign' for Ba1 as Strong and Bs1 as Supporting`, () => { + // arrange: nothing, only test check + + // act: const AcmgCriteriaState = new MultiSourceAcmgCriteriaState() // Set Ba1 and Bs1 to present AcmgCriteriaState.setPresence(StateSource.User, AcmgCriteria.BA1, Presence.Present) @@ -709,6 +864,7 @@ describe.concurrent( AcmgEvidenceLevel.BenignSupporting ) + // assert: // Expect to return 'Likely benign' expect(AcmgCriteriaState.getAcmgClass()).toEqual(['Likely benign', false]) }) diff --git a/frontend/src/lib/__tests__/genomeBuild.spec.ts b/frontend/src/lib/__tests__/genomeBuild.spec.ts index 77f8f34b..6d73fe07 100644 --- a/frontend/src/lib/__tests__/genomeBuild.spec.ts +++ b/frontend/src/lib/__tests__/genomeBuild.spec.ts @@ -11,10 +11,18 @@ import { describe.concurrent('constants', () => { it('genomeBuildAliases should have the well-known keys', () => { + // arrange: nothing to do + // act: nothing to do + + // assert: expect(Object.keys(GENOME_BUILD_ALIASES)).toEqual(['hg19', 'grch37', 'hg38', 'grch38']) }) it('CHROM_REFSEQ_37 should have the well-known keys', () => { + // arrange: nothing to do + // act: nothing to do + + // assert: expect(Object.keys(CHROM_REFSEQ_37)).toEqual([ '1', '2', @@ -45,6 +53,10 @@ describe.concurrent('constants', () => { }) it('CHROM_REFSEQ_38 should have the well-known keys', () => { + // arrange: nothing to do + // act: nothing to do + + // assert: expect(Object.keys(CHROM_REFSEQ_38)).toEqual([ '1', '2', @@ -75,6 +87,10 @@ describe.concurrent('constants', () => { }) it('CHROM_LENGTHS_37 should have the well-known keys', () => { + // arrange: nothing to do + // act: nothing to do + + // assert: expect(Object.keys(CHROM_LENGTHS_37)).toEqual([ '1', '2', @@ -105,6 +121,10 @@ describe.concurrent('constants', () => { }) it('CHROM_LENGTHS_38 should have the well-known keys', () => { + // arrange: nothing to do + // act: nothing to do + + // assert: expect(Object.keys(CHROM_LENGTHS_38)).toEqual([ '1', '2', @@ -135,6 +155,10 @@ describe.concurrent('constants', () => { }) it('lengths of 37 and 38 differ but not in chrMT', () => { + // arrange: nothing to do + // act: nothing to do + + // assert: expect(CHROM_LENGTHS_37.X).not.toEqual(CHROM_LENGTHS_38.X) expect(CHROM_LENGTHS_37.Y).not.toEqual(CHROM_LENGTHS_38.Y) expect(CHROM_LENGTHS_37.MT).toEqual(CHROM_LENGTHS_38.MT) @@ -143,24 +167,44 @@ describe.concurrent('constants', () => { describe.concurrent('refseqToGenomeBuild', () => { it('should return grch37 for NC_000001.10', () => { + // arrange: nothing to do + // act: nothing to do + + // assert: expect(refseqToGenomeBuild('NC_000001.10')).toEqual('grch37') }) it('should return grch38 for NC_000001.11', () => { + // arrange: nothing to do + // act: nothing to do + + // assert: expect(refseqToGenomeBuild('NC_000001.11')).toEqual('grch38') }) it('should return grch38 for NC_012920.1', () => { + // arrange: nothing to do + // act: nothing to do + + // assert: expect(refseqToGenomeBuild('NC_012920.1')).toEqual('grch38') }) it('should work case insensitive', () => { + // arrange: nothing to do + // act: nothing to do + + // assert: expect(refseqToGenomeBuild('nc_000001.10')).toEqual('grch37') expect(refseqToGenomeBuild('nc_000001.11')).toEqual('grch38') expect(refseqToGenomeBuild('nc_012920.1')).toEqual('grch38') }) it('should throw an error for NC_000001.12', () => { + // arrange: nothing to do + // act: nothing to do + + // assert: expect(() => refseqToGenomeBuild('NC_000001.12')).toThrow( 'Unknown RefSeq identifier: NC_000001.12' ) diff --git a/frontend/src/lib/__tests__/genomicVars.spec.ts b/frontend/src/lib/__tests__/genomicVars.spec.ts index 7891ea46..85bf5e62 100644 --- a/frontend/src/lib/__tests__/genomicVars.spec.ts +++ b/frontend/src/lib/__tests__/genomicVars.spec.ts @@ -25,6 +25,10 @@ import { describe.concurrent('regular expression REGEX_GNOMAD_VARIANT', () => { it('should match variants with chromosome name only', () => { + // arrange: nothing to do + // act: nothing to do + + // assert: expect('chr1-100-AT-TG').toMatch(REGEX_GNOMAD_VARIANT) expect('chr22-100-AT-TG').toMatch(REGEX_GNOMAD_VARIANT) expect('chrX-100-AT-TG').toMatch(REGEX_GNOMAD_VARIANT) @@ -46,6 +50,10 @@ describe.concurrent('regular expression REGEX_GNOMAD_VARIANT', () => { }) it('should match variants with valid genome releases name', () => { + // arrange: nothing to do + // act: nothing to do + + // assert: expect('hg19-chr1-100-AT-TG').toMatch(REGEX_GNOMAD_VARIANT) expect('hg19-chr22-100-AT-TG').toMatch(REGEX_GNOMAD_VARIANT) expect('hg38-chrX-100-AT-TG').toMatch(REGEX_GNOMAD_VARIANT) @@ -67,6 +75,10 @@ describe.concurrent('regular expression REGEX_GNOMAD_VARIANT', () => { }) it('should match variants with invalid genome releases name', () => { + // arrange: nothing to do + // act: nothing to do + + // assert: expect('T2T-Y-100-AT-TG').toMatch(REGEX_GNOMAD_VARIANT) expect('T2T-MT-100-AT-TG').toMatch(REGEX_GNOMAD_VARIANT) @@ -82,6 +94,10 @@ describe.concurrent('regular expression REGEX_GNOMAD_VARIANT', () => { describe.concurrent('regular expression REGEX_CANONICAL_SPDI', () => { it('should match correctly formatted variants only', () => { + // arrange: nothing to do + // act: nothing to do + + // assert: expect('NC_000001.11:100:AT:TG').toMatch(REGEX_CANONICAL_SPDI) expect('NC_999999.999:100:AT:TG').toMatch(REGEX_CANONICAL_SPDI) expect('NC_000000.0:100:AT:TG').toMatch(REGEX_CANONICAL_SPDI) @@ -103,6 +119,10 @@ describe.concurrent('regular expression REGEX_CANONICAL_SPDI', () => { describe.concurrent('regular expression REGEX_RELAXED_SPDI', () => { it('should match variants with chromosome name only', () => { + // arrange: nothing to do + // act: nothing to do + + // assert: expect('chr1:100:AT:TG').toMatch(REGEX_RELAXED_SPDI) expect('chr22:100:AT:TG').toMatch(REGEX_RELAXED_SPDI) expect('chrX:100:AT:TG').toMatch(REGEX_RELAXED_SPDI) @@ -124,6 +144,10 @@ describe.concurrent('regular expression REGEX_RELAXED_SPDI', () => { }) it('should match variants with valid genome releases name', () => { + // arrange: nothing to do + // act: nothing to do + + // assert: expect('hg19:chr1:100:AT:TG').toMatch(REGEX_RELAXED_SPDI) expect('hg19:chr22:100:AT:TG').toMatch(REGEX_RELAXED_SPDI) expect('hg38:chrX:100:AT:TG').toMatch(REGEX_RELAXED_SPDI) @@ -145,6 +169,10 @@ describe.concurrent('regular expression REGEX_RELAXED_SPDI', () => { }) it('should match variants with invalid genome releases name', () => { + // arrange: nothing to do + // act: nothing to do + + // assert: expect('T2T:Y:100:AT:TG').toMatch(REGEX_RELAXED_SPDI) expect('T2T:MT:100:AT:TG').toMatch(REGEX_RELAXED_SPDI) @@ -160,6 +188,10 @@ describe.concurrent('regular expression REGEX_RELAXED_SPDI', () => { describe.concurrent('regular expression REGEX_DBSNP_ID', () => { it('should match correctly formatted rs IDs only', () => { + // arrange: nothing to do + // act: nothing to do + + // assert: expect('rs1').toMatch(REGEX_DBSNP_ID) expect('rs99999999999').toMatch(REGEX_DBSNP_ID) expect('sr1').not.toMatch(REGEX_DBSNP_ID) @@ -169,6 +201,10 @@ describe.concurrent('regular expression REGEX_DBSNP_ID', () => { describe.concurrent('regular expression REGEX_CLINVAR_ID', () => { it('should match correctly formatted IDs only', () => { + // arrange: nothing to do + // act: nothing to do + + // assert: expect('VCV000148363.2').toMatch(REGEX_CLINVAR_ID) expect('VCV000000000.0').toMatch(REGEX_CLINVAR_ID) expect('RCV000148363.2').toMatch(REGEX_CLINVAR_ID) @@ -194,6 +230,10 @@ describe.concurrent('regular expression REGEX_CLINVAR_ID', () => { describe.concurrent('regular expression REGEX_CNV_COLON', () => { it('should match variants with chromosome name only', () => { + // arrange: nothing to do + // act: nothing to do + + // assert: expect('DEL:chr1:100:200').toMatch(REGEX_CNV_COLON) expect('DEL:1:100:200').toMatch(REGEX_CNV_COLON) expect('DUP:chr1:100:200').toMatch(REGEX_CNV_COLON) @@ -210,6 +250,10 @@ describe.concurrent('regular expression REGEX_CNV_COLON', () => { }) it('should match variants with valid genome releases name', () => { + // arrange: nothing to do + // act: nothing to do + + // assert: expect('DEL:GRCh37:chr1:100:200').toMatch(REGEX_CNV_COLON) expect('DEL:GRCh38:1:100:200').toMatch(REGEX_CNV_COLON) expect('DUP:hg19:chr1:100:200').toMatch(REGEX_CNV_COLON) @@ -226,6 +270,10 @@ describe.concurrent('regular expression REGEX_CNV_COLON', () => { }) it('should match variants with RefSeq identifiers', () => { + // arrange: nothing to do + // act: nothing to do + + // assert: expect('DEL:NC_000001.10:100:200').toMatch(REGEX_CNV_COLON) expect('DEL:NC_000001.11:100:200').toMatch(REGEX_CNV_COLON) expect('DUP:NC_000001.10:100:200').toMatch(REGEX_CNV_COLON) @@ -242,6 +290,10 @@ describe.concurrent('regular expression REGEX_CNV_COLON', () => { }) it('should match variants with invalid genome releases name', () => { + // arrange: nothing to do + // act: nothing to do + + // assert: expect('DEL:T2T:chr1:100:200').toMatch(REGEX_CNV_COLON) expect('DEL:T2T:chr1:100:200'.match(REGEX_CNV_COLON)?.groups).toEqual({ @@ -257,6 +309,10 @@ describe.concurrent('regular expression REGEX_CNV_COLON', () => { describe.concurrent('regular expression REGEX_CNV_HYPEN', () => { it('should match variants with chromosome name only', () => { + // arrange: nothing to do + // act: nothing to do + + // assert: expect('DEL-chr1-100-200').toMatch(REGEX_CNV_HYPHEN) expect('DEL-1-100-200').toMatch(REGEX_CNV_HYPHEN) expect('DUP-chr1-100-200').toMatch(REGEX_CNV_HYPHEN) @@ -273,6 +329,10 @@ describe.concurrent('regular expression REGEX_CNV_HYPEN', () => { }) it('should match variants with valid genome releases name', () => { + // arrange: nothing to do + // act: nothing to do + + // assert: expect('DEL-GRCh37-chr1-100-200').toMatch(REGEX_CNV_HYPHEN) expect('DEL-GRCh38-1-100-200').toMatch(REGEX_CNV_HYPHEN) expect('DUP-hg19-chr1-100-200').toMatch(REGEX_CNV_HYPHEN) @@ -289,6 +349,10 @@ describe.concurrent('regular expression REGEX_CNV_HYPEN', () => { }) it('should match variants with RefSeq identifiers', () => { + // arrange: nothing to do + // act: nothing to do + + // assert: expect('DEL-NC_000001.10-100-200').toMatch(REGEX_CNV_HYPHEN) expect('DEL-NC_000001.11-100-200').toMatch(REGEX_CNV_HYPHEN) expect('DUP-NC_000001.10-100-200').toMatch(REGEX_CNV_HYPHEN) @@ -305,6 +369,10 @@ describe.concurrent('regular expression REGEX_CNV_HYPEN', () => { }) it('should match variants with invalid genome releases name', () => { + // arrange: nothing to do + // act: nothing to do + + // assert: expect('DEL-T2T-chr1-100-200').toMatch(REGEX_CNV_HYPHEN) expect('DEL-T2T-chr1-100-200'.match(REGEX_CNV_HYPHEN)?.groups).toEqual({ @@ -320,6 +388,10 @@ describe.concurrent('regular expression REGEX_CNV_HYPEN', () => { describe.concurrent('regular expression REGEX_CNV_ISCN_2020', () => { it('should match valid strings', () => { + // arrange: nothing to do + // act: nothing to do + + // assert: expect('arr[GRCh37] 2q12.2q13 (107132950_110427254)x1').toMatch(REGEX_CNV_ISCN_2020) expect( 'arr[GRCh37] 2q12.2q13 (107132950_110427254)x1'.match(REGEX_CNV_ISCN_2020)?.groups @@ -368,6 +440,7 @@ describe.concurrent('validateSequenceVariant()', () => { ['1', 249250621, 'AT'], ['1', 249250622, 'A'] ])('throws on %s-%d-%s', (chrom, pos, del) => { + // arrange: const variant = { genomeBuild: 'grch37' as GenomeBuild, chrom, @@ -376,6 +449,10 @@ describe.concurrent('validateSequenceVariant()', () => { ins: 'TG', userRepr: 'TEST' } + + // act: nothing to do + + // assert: expect(() => validateSeqvar(variant)).toThrow() }) @@ -383,6 +460,7 @@ describe.concurrent('validateSequenceVariant()', () => { ['1', 249250620, 'AT'], ['1', 249250621, 'A'] ])('work on %s-%d-%s', (chrom, pos, del) => { + // arrange: const variant = { genomeBuild: 'grch37' as GenomeBuild, chrom, @@ -391,6 +469,10 @@ describe.concurrent('validateSequenceVariant()', () => { ins: 'TG', userRepr: 'TEST' } + + // act: nothing to do + + // assert: expect(validateSeqvar(variant)).toEqual(variant) }) }) @@ -404,6 +486,10 @@ describe.concurrent('parseSeparatedSeqvar()', () => { ['m-100-at-tg', 'grch37', 'MT'], ['mt-100-at-tg', 'grch37', 'MT'] ])('hyphen-separated result for %s', (variant, expectedGenomeRelease, expectedChrom) => { + // arrange: nothing to do + // act: nothing to do + + // assert: expect(parseSeparatedSeqvar(variant)).toEqual({ genomeBuild: expectedGenomeRelease, chrom: expectedChrom, @@ -422,6 +508,10 @@ describe.concurrent('parseSeparatedSeqvar()', () => { ['m:100:at:tg', 'grch37', 'MT'], ['mt:100:at:tg', 'grch37', 'MT'] ])('colon-separated result for %s', (variant, expectedGenomeRelease, expectedChrom) => { + // arrange: nothing to do + // act: nothing to do + + // assert: expect(parseSeparatedSeqvar(variant)).toEqual({ genomeBuild: expectedGenomeRelease, chrom: expectedChrom, @@ -440,6 +530,10 @@ describe.concurrent('parseCanonicalSpdiSeqvar()', () => { ['NC_000023.10:100:AT:TG', 'grch37', 'X'], ['NC_012920.1:100:at:tg', 'grch37', 'MT'] ])('result for %s', (variant, expectedGenomeRelease, expectedChrom) => { + // arrange: nothing to do + // act: nothing to do + + // assert: expect(parseCanonicalSpdiSeqvar(variant)).toEqual({ genomeBuild: expectedGenomeRelease, chrom: expectedChrom, @@ -453,6 +547,10 @@ describe.concurrent('parseCanonicalSpdiSeqvar()', () => { describe.concurrent('parseSeparatedStrucvar()', () => { it('parse DEL:1:100:200', () => { + // arrange: nothing to do + // act: nothing to do + + // assert: expect(parseSeparatedStrucvar('DEL:1:100:200')).toEqual({ chrom: '1', copyNumber: undefined, @@ -465,6 +563,10 @@ describe.concurrent('parseSeparatedStrucvar()', () => { }) it('parse DEL:GRCh37:1:100:200', () => { + // arrange: nothing to do + // act: nothing to do + + // assert: expect(parseSeparatedStrucvar('DEL:1:100:200')).toEqual({ chrom: '1', copyNumber: undefined, @@ -477,6 +579,10 @@ describe.concurrent('parseSeparatedStrucvar()', () => { }) it('parse DEL:NC_000001.11:100:200', () => { + // arrange: nothing to do + // act: nothing to do + + // assert: expect(parseSeparatedStrucvar('DEL:NC_000001.11:100:200')).toEqual({ chrom: '1', copyNumber: undefined, @@ -489,6 +595,10 @@ describe.concurrent('parseSeparatedStrucvar()', () => { }) it('parse DUP-NC_000001.11-100-200', () => { + // arrange: nothing to do + // act: nothing to do + + // assert: expect(parseSeparatedStrucvar('DUP-NC_000001.11-100-200')).toEqual({ chrom: '1', copyNumber: undefined, @@ -501,20 +611,36 @@ describe.concurrent('parseSeparatedStrucvar()', () => { }) it('throws when start > stop', () => { + // arrange: nothing to do + // act: nothing to do + + // assert: expect(() => parseSeparatedStrucvar('DEL:NC_000001.11:200:100')).toThrow() }) it('throws when start > length', () => { + // arrange: nothing to do + // act: nothing to do + + // assert: expect(() => parseSeparatedStrucvar('DEL:NC_000001.11:249250621:249250621')).toThrow() }) it('throws when stop > length', () => { + // arrange: nothing to do + // act: nothing to do + + // assert: expect(() => parseSeparatedStrucvar('DEL:NC_000001.11:1:249250623')).toThrow() }) }) describe.concurrent('parseIscnCnv', () => { it('should match valid strings', () => { + // arrange: nothing to do + // act: nothing to do + + // assert: expect(parseIscnCnv('arr[GRCh37] 2q12.2q13 (107132950_110427254)x1')).toEqual({ chrom: '2', copyNumber: 1, @@ -574,6 +700,10 @@ describe.concurrent('parseIscnCnv', () => { }) it('should throw on invalid positions', () => { + // arrange: nothing to do + // act: nothing to do + + // assert: expect(() => parseSeparatedStrucvar('arr[GRCh37] 2q12.2q13 (200_100)x1')).toThrow() expect(() => parseSeparatedStrucvar('arr[GRCh37] 2q12.2q13 (243199374_243199374)x1')).toThrow() expect(() => parseSeparatedStrucvar('arr[GRCh37] 2q12.2q13 (1_243199374)x1')).toThrow() @@ -586,11 +716,17 @@ describe.concurrent('parseIscnCnv', () => { describe.concurrent('SeqvarImpl', () => { it('should work properly with toName()', () => { + // arrange: const variant = new SeqvarImpl('grch37', '1', 100, 'AT', 'TG') + + // act: nothing to do + + // assert: expect(variant.toName()).toEqual('grch37-1-100-AT-TG') }) it('should be constructable with seqvarImplFromSeqvar()', () => { + // arrange: const seqvar: Seqvar = { genomeBuild: 'grch37', chrom: '1', @@ -600,17 +736,27 @@ describe.concurrent('SeqvarImpl', () => { userRepr: 'TEST' } const variant = seqvarImplFromSeqvar(seqvar) + + // act: nothing to do + + // assert: expect(variant).toEqual(seqvar) }) }) describe.concurrent('LinearStrucvarImpl', () => { it('should work properly with toName()', () => { + // arrange: const variant = new LinearStrucvarImpl('DEL', 'grch37', '1', 100, 200, undefined, undefined) + + // act: nothing to do + + // assert: expect(variant.toName()).toEqual('DEL-grch37-1-100-200') }) it('should be constructable with seqvarImplFromSeqvar()', () => { + // arrange: const strucvar: LinearStrucvar = { svType: 'DEL', genomeBuild: 'grch37', @@ -620,6 +766,10 @@ describe.concurrent('LinearStrucvarImpl', () => { userRepr: 'DEL-grch37-1-100-200' } const variant = linearStrucvarImplFromLinearStrucvar(strucvar) + + // act: nothing to do + + // assert: expect(variant).toEqual(strucvar) }) }) diff --git a/frontend/src/lib/__tests__/query.spec.ts b/frontend/src/lib/__tests__/query.spec.ts index 200e1267..4607d970 100644 --- a/frontend/src/lib/__tests__/query.spec.ts +++ b/frontend/src/lib/__tests__/query.spec.ts @@ -24,6 +24,7 @@ describe.concurrent('Variant lookup with dotty', () => { }) it('should return a gene', async () => { + // arrange: fetchMocker.mockResponseOnce( JSON.stringify({ success: true, @@ -36,13 +37,20 @@ describe.concurrent('Variant lookup with dotty', () => { }) ) + // act: const result = await lookupWithDotty(seqVar.userRepr, seqVar.genomeBuild) + + // assert: expect(result).toStrictEqual(seqVar) }) it('should throw an error if dotty fails', async () => { + // arrange: fetchMocker.mockResponseOnce(JSON.stringify({ success: false })) + // act: nothing to do + + // assert: await expect(lookupWithDotty(seqVar.userRepr, seqVar.genomeBuild)).rejects.toThrow() }) }) @@ -64,6 +72,7 @@ describe.concurrent('Resolve seqvar from the given query', () => { }) it('should return a gene', async () => { + // arrange: fetchMocker.mockResponseOnce( JSON.stringify({ success: true, @@ -76,7 +85,10 @@ describe.concurrent('Resolve seqvar from the given query', () => { }) ) + // act: const result = await resolveSeqvar(seqVar.userRepr, seqVar.genomeBuild) + + // assert: expect(result).toStrictEqual(seqVar) }) }) @@ -99,11 +111,15 @@ describe.concurrent('Resolve strucvar from the given query', () => { }) it('should return a gene', async () => { + // arrange: fetchMocker.mockResponseOnce(JSON.stringify({ success: true, value: strucVar })) + // act: const result = resolveStrucvar(strucVar.userRepr, strucVar.genomeBuild) const expected = structuredClone(strucVar) expected.userRepr = 'DEL-GRCh37-17-41176312-41277500' + + // assert: expect(result).toStrictEqual(expected) }) }) @@ -115,6 +131,7 @@ describe.concurrent('Gene lookup', () => { }) it('should return a gene', async () => { + // arrange: fetchMocker.mockResponseOnce( JSON.stringify({ success: true, @@ -135,13 +152,20 @@ describe.concurrent('Gene lookup', () => { }) ) + // act: const result = await lookupGene('BRCA1') + + // assert: expect(result).toContain('BRCA1') }) it('should throw an error if dotty fails', async () => { + // arrange: fetchMocker.mockResponseOnce(JSON.stringify({ success: false })) + // act: nothing to do + + // assert: await expect(lookupGene('BRCA1')).rejects.toThrow() }) }) diff --git a/frontend/src/lib/__tests__/testUtils.spec.ts b/frontend/src/lib/__tests__/testUtils.spec.ts index 5b6bd725..87dc328a 100644 --- a/frontend/src/lib/__tests__/testUtils.spec.ts +++ b/frontend/src/lib/__tests__/testUtils.spec.ts @@ -2,59 +2,80 @@ import { describe, expect, it } from 'vitest' import { setupMountedComponents } from '@/lib/testUtils' +// Test data const TestComponent = { template: '', __name: 'TestComponent' } describe.concurrent('Test Utils - setupMountedComponents', async () => { it('should mount component with template', async () => { + // arrange: const { wrapper } = await setupMountedComponents( { component: TestComponent }, { initialStoreState: { state: { count: 42 } } } ) + // act: nothing, only test rendering + + // assert: expect(wrapper.exists()).toBe(true) }) it('should mount component without template', async () => { + // arrange: const { wrapper } = await setupMountedComponents( { component: TestComponent }, { initialStoreState: { count: 42 } } ) + // act: nothing, only test rendering + + // assert: expect(wrapper.exists()).toBe(true) }) it('should mock router push method', async () => { + // arrange: const { router } = await setupMountedComponents( { component: TestComponent }, { initialStoreState: { count: 42 } } ) + // act: router.push('/new-route') + + // assert: expect(router.push).toHaveBeenCalledWith('/new-route') }) it('should create vuetify, router, and pinia instances', async () => { + // arrange: const { wrapper, router, pinia } = await setupMountedComponents( { component: TestComponent }, { initialStoreState: { count: 42 } } ) + // act: nothing, only test rendering + + // assert: expect(wrapper.exists()).toBe(true) expect(router).toBeDefined() expect(pinia).toBeDefined() }) it('should set props when rendering without template', async () => { + // arrange: const TestComponent = { props: ['message'], template: '
{{ message }}
', __name: 'TestComponent' } - const { wrapper } = await setupMountedComponents( { component: TestComponent }, { props: { message: 'Hello, Props!' } } ) + + // act: nothing, only test rendering + + // assert: expect(wrapper.text()).toBe('Hello, Props!') }) }) diff --git a/frontend/src/lib/__tests__/utils.spec.ts b/frontend/src/lib/__tests__/utils.spec.ts index e2f3e73c..8ff12b9e 100644 --- a/frontend/src/lib/__tests__/utils.spec.ts +++ b/frontend/src/lib/__tests__/utils.spec.ts @@ -1,4 +1,4 @@ -import { describe, expect, it, test } from 'vitest' +import { describe, expect, it } from 'vitest' import { type GenomeBuild } from '@/lib/genomeBuilds' import { type Seqvar } from '@/lib/genomicVars' @@ -27,73 +27,133 @@ const seqVar: Seqvar = { describe.concurrent('roundIt method', () => { it('should round a positive value with default digits', () => { + // arrange: nothing to do + + // act: const result = roundIt(3.14159) + + // assert: expect(result).toBe('3.14') }) it('should round a positive value with specified digits', () => { + // arrange: nothing to do + + // act: const result = roundIt(3.14159, 3) + + // assert: expect(result).toBe('3.142') }) it('should handle zero value', () => { + // arrange: nothing to do + + // act: const result = roundIt(0) + + // assert: expect(result).toBe("0") }) it('should handle NaN value', () => { + // arrange: nothing to do + + // act: const result = roundIt(NaN) + + // assert: expect(result).toBe("0") }) it('should add label to title if provided', () => { + // arrange: nothing to do + + // act: const result = roundIt(5.6789, 2, 'Value') + + // assert: expect(result).toBe('5.68') }) it('should handle negative value', () => { + // arrange: nothing to do + + // act: const result = roundIt(-10.12345) + + // assert: expect(result).toBe('-10.12') }) }) describe.concurrent('separateIt method', () => { it('should separate a positive value with default separator', () => { + // arrange: nothing to do + + // act: const result = separateIt(123456789) + + // assert: expect(result).toBe('123 456 789') }) it('should separate a positive value with specified separator', () => { + // arrange: nothing to do + + // act: const result = separateIt(123456789, ',') + + // assert: expect(result).toBe('123,456,789') }) it('should handle zero value', () => { + // arrange: nothing to do + + // act: const result = separateIt(0) + + // assert: expect(result).toBe('0') }) it('should handle float value', () => { + // arrange: nothing to do + + // act: const result = separateIt(123456789.12345) + + // assert: expect(result).toBe('123 456 789') }) it('should handle values less then 0', () => { + // arrange: nothing to do + + // act: const result = separateIt(0.0134) + + // assert: expect(result).toBe('0') }) }) describe.concurrent('isVariantMt method', () => { it('should return true if mitochondrial chromosome', () => { + // arrange: seqVar.chrom = 'MT' - const result_MT = isVariantMt(seqVar) seqVar.chrom = 'M' - const result_M = isVariantMt(seqVar) seqVar.chrom = 'chrMT' - const result_chrMT = isVariantMt(seqVar) seqVar.chrom = 'chrM' + + // act: + const result_MT = isVariantMt(seqVar) + const result_M = isVariantMt(seqVar) + const result_chrMT = isVariantMt(seqVar) const result_chrM = isVariantMt(seqVar) + + // assert: expect(result_MT).toBe(true) expect(result_M).toBe(true) expect(result_chrMT).toBe(true) @@ -101,65 +161,115 @@ describe.concurrent('isVariantMt method', () => { }) it('should return false if not mitochondrial chromosome', () => { + // arrange: seqVar.chrom = '1' + + // act: const result = isVariantMt(seqVar) + + // assert: expect(result).toBe(false) }) }) describe.concurrent('isVariantMtHomopolymer method', () => { it('should return true if mitochondrial homopolymer', () => { + // arrange: seqVar.chrom = 'MT' seqVar.pos = 70 + + // act: const result = isVariantMtHomopolymer(seqVar) + + // assert: expect(result).toBe(true) }) it('should return false if not mitochondrial homopolymer (chromosome)', () => { + // arrange: seqVar.chrom = '1' seqVar.pos = 70 + + // act: const result = isVariantMtHomopolymer(seqVar) + + // assert: expect(result).toBe(false) }) it('should return false if not mitochondrial homopolymer (position)', () => { + // arrange: seqVar.chrom = 'MT' seqVar.pos = 1 + + // act: const result = isVariantMtHomopolymer(seqVar) + + // assert: expect(result).toBe(false) }) }) describe.concurrent('removeCommasFromNumbers method', () => { it('should remove commas from numbers', () => { + // arrange: nothing to do + + // act: const result = removeCommasFromNumbers('1,234,567,890') + + // assert: expect(result).toBe('1234567890') }) it('should return the same string if no commas', () => { + // arrange: nothing to do + + // act: const result = removeCommasFromNumbers('1234567890') + + // assert: expect(result).toBe('1234567890') }) it('should return the same string if empty', () => { + // arrange: nothing to do + + // act: const result = removeCommasFromNumbers('') + + // assert: expect(result).toBe('') }) it('should not remove commas from strings', () => { + // arrange: nothing to do + + // act: const result = removeCommasFromNumbers('foo,foo,bar') + + // assert: expect(result).toBe('foo,foo,bar') }) it('should not remove commas from numbers in strings', () => { + // arrange: nothing to do + + // act: const result = removeCommasFromNumbers('foo,1,234,567,890,bar') + + // assert: expect(result).toBe('foo,1234567890,bar') }) }) describe.concurrent('infoFromQuery method', () => { it('should return info from query', () => { + // arrange: nothing to do + + // act: const result = infoFromQuery('chr37:12345:A:G') + + // assert: expect(result).toEqual({ chromosome: 'chr37', pos: '12345', @@ -170,7 +280,12 @@ describe.concurrent('infoFromQuery method', () => { }) it('should return empty object if no query', () => { + // arrange: nothing to do + + // act: const result = infoFromQuery('') + + // assert: expect(result).toEqual({ chromosome: '', pos: undefined, @@ -183,22 +298,31 @@ describe.concurrent('infoFromQuery method', () => { describe.concurrent('copy method', () => { it('should return a JSON object for given dict', () => { + // arrange: nothing to do + + // act: const result = copy({ foo: 'bar' }) + + // assert: expect(result).toEqual({ foo: 'bar' }) }) }) describe.concurrent('extractDbnsfpMimDiseaseId', () => { - test.each([ + it.each([ ['[MIM:616921] Dyskinesia, limb and orofacial, infantile-onset', '616921'], ['[MIM:616921] Dyskinesia, limb and orofacial, infantile-onset [recessive?]', '616921'] ])('%s => %s', (lhs, rhs) => { + // arrange: nothing to do + // act: nothing to do + + // assert: expect(extractDbnsfpMimDiseaseId(lhs)).toEqual(rhs) }) }) describe.concurrent('transformDbnsfpMimDiseaseId', () => { - test.each([ + it.each([ [ '[MIM:616921]Dyskinesia, limb and orofacial, infantile-onset', '[MIM:616921] Dyskinesia, limb and orofacial, infantile-onset', @@ -220,6 +344,10 @@ describe.concurrent('transformDbnsfpMimDiseaseId', () => { false ] ])('should work correctly', (lhs, rhs, showTermIds) => { + // arrange: nothing to do + // act: nothing to do + + // assert: expect(transformDbnsfpMimDiseaseId(lhs, showTermIds)).toEqual(rhs) }) }) diff --git a/frontend/src/stores/__tests__/bookmarks.spec.ts b/frontend/src/stores/__tests__/bookmarks.spec.ts index c877ce9d..8f6a6021 100644 --- a/frontend/src/stores/__tests__/bookmarks.spec.ts +++ b/frontend/src/stores/__tests__/bookmarks.spec.ts @@ -15,13 +15,18 @@ describe.concurrent('User Store', () => { }) it('should have initial state', () => { + // arrange: const store = useBookmarksStore() + // act: nothing to do + + // assert: expect(store.storeState).toBe(StoreState.Initial) expect(store.bookmarks).toStrictEqual([]) }) it('should load bookmarks', async () => { + // arrange: const mockBookmarks: BookmarkData[] = [ { user: '2c0a153e-5e8c-11ee-8c99-0242ac120002', @@ -30,93 +35,112 @@ describe.concurrent('User Store', () => { id: '2c0a153e-5e8c-11ee-8c99-0242ac120001' } ] - - const store = useBookmarksStore() fetchMocker.mockResponse(JSON.stringify(mockBookmarks)) + const store = useBookmarksStore() + // act: await store.loadBookmarks() + // assert: expect(store.storeState).toBe(StoreState.Active) expect(store.bookmarks).toEqual(mockBookmarks) }) it('should handle error when loading bookmarks', async () => { + // arrange: // Disable error logging vi.spyOn(console, 'error').mockImplementation(() => {}) - - const store = useBookmarksStore() fetchMocker.mockReject(new Error('Internal Server Error')) + const store = useBookmarksStore() + // act: await store.loadBookmarks() + // assert: expect(store.storeState).toBe(StoreState.Error) expect(store.bookmarks).toStrictEqual([]) }) it('should delete bookmark', async () => { - const store = useBookmarksStore() + // arrange: fetchMocker.mockResponse(JSON.stringify({})) + const store = useBookmarksStore() + // act: await store.deleteBookmark('seqvar', 'HGNC:1100') + // assert: expect(store.storeState).toBe(StoreState.Active) expect(store.bookmarks).toStrictEqual({}) }) it('should handle error when deleting bookmark', async () => { + // arrange: // Disable error logging vi.spyOn(console, 'error').mockImplementation(() => {}) - - const store = useBookmarksStore() fetchMocker.mockReject(new Error('Internal Server Error')) + const store = useBookmarksStore() + // act: await store.deleteBookmark('seqvar', 'HGNC:1100') + // assert: expect(store.storeState).toBe(StoreState.Error) expect(store.bookmarks).toStrictEqual([]) }) it('should create bookmark', async () => { - const store = useBookmarksStore() + // arrange: fetchMocker.mockResponse(JSON.stringify({ bookmark: 'created' })) + const store = useBookmarksStore() + // act: await store.createBookmark('seqvar', 'HGNC:1100') + // assert: expect(store.storeState).toBe(StoreState.Active) expect(store.bookmarks).toStrictEqual({ bookmark: 'created' }) }) it('should handle error when creating bookmark', async () => { + // arrange: // Disable error logging vi.spyOn(console, 'error').mockImplementation(() => {}) - - const store = useBookmarksStore() fetchMocker.mockReject(new Error('Internal Server Error')) + const store = useBookmarksStore() + // act: await store.createBookmark('seqvar', 'HGNC:1100') + // assert: expect(store.storeState).toBe(StoreState.Error) expect(store.bookmarks).toStrictEqual([]) }) it('should fetch bookmark', async () => { + // arrange: + fetchMocker.mockResponse(JSON.stringify({ bookmark: 'created' })) const store = useBookmarksStore() store.storeState = StoreState.Active - fetchMocker.mockResponse(JSON.stringify({ bookmark: 'created' })) + // act: const result = await store.fetchBookmark('seqvar', 'HGNC:1100') + // assert: expect(store.storeState).toBe(StoreState.Active) expect(result).toStrictEqual({ bookmark: 'created' }) }) it('should handle error when fetching bookmark', async () => { + // arrange: + fetchMocker.mockResponse(JSON.stringify({ detail: 'Internal Server Error' }), { status: 500 }) const store = useBookmarksStore() store.storeState = StoreState.Active - fetchMocker.mockResponse(JSON.stringify({ detail: 'Internal Server Error' }), { status: 500 }) + // act: await store.fetchBookmark('seqvar', 'HGNC:1100') + // assert: expect(store.storeState).toBe(StoreState.Active) expect(store.bookmarks).toStrictEqual([]) }) diff --git a/frontend/src/stores/__tests__/cadaPrio.spec.ts b/frontend/src/stores/__tests__/cadaPrio.spec.ts index 6f444e91..cb634c1c 100644 --- a/frontend/src/stores/__tests__/cadaPrio.spec.ts +++ b/frontend/src/stores/__tests__/cadaPrio.spec.ts @@ -15,31 +15,40 @@ describe.concurrent('Cada Prio Store', () => { }) it('should have initial state', () => { + // arrange: const store = useCadaPrioStore() + // act: nothing to do + + // assert: expect(store.storeState).toBe(StoreState.Initial) expect(store.geneRanking).toBe(null) }) it('should predict gene impact', async () => { - const store = useCadaPrioStore() + // arrange: fetchMocker.mockResponse(JSON.stringify({ result: 'pathogenic' })) + const store = useCadaPrioStore() + // act: await store.loadData(['HP:0000001']) + // assert: expect(store.storeState).toBe(StoreState.Active) expect(store.geneRanking).toStrictEqual({ result: 'pathogenic' }) }) it('should handle error when predicting gene impact', async () => { + // arrange: // Disable error logging vi.spyOn(console, 'error').mockImplementation(() => {}) - - const store = useCadaPrioStore() fetchMocker.mockReject(new Error('Internal Server Error')) + const store = useCadaPrioStore() + // act: await store.loadData(['HP:0000001']) + // assert: expect(store.storeState).toBe(StoreState.Error) expect(store.geneRanking).toBe(null) }) diff --git a/frontend/src/stores/__tests__/geneInfo.spec.ts b/frontend/src/stores/__tests__/geneInfo.spec.ts index 516c2eb1..157a681e 100644 --- a/frontend/src/stores/__tests__/geneInfo.spec.ts +++ b/frontend/src/stores/__tests__/geneInfo.spec.ts @@ -15,8 +15,12 @@ describe.concurrent('geneInfo Store', () => { }) it('should have initial state', () => { + // arrange: const store = useGeneInfoStore() + // act: nothing to do + + // assert: expect(store.storeState).toBe(StoreState.Initial) expect(store.hgncId).toBe(null) expect(store.geneInfo).toBe(null) @@ -25,13 +29,16 @@ describe.concurrent('geneInfo Store', () => { }) it('should clear state', () => { + // arrange: const store = useGeneInfoStore() store.storeState = StoreState.Active store.hgncId = 'BRCA1' store.geneInfo = JSON.parse(JSON.stringify({ gene: 'info' })) + // act: store.clearData() + // assert: expect(store.storeState).toBe(StoreState.Initial) expect(store.hgncId).toBe(null) expect(store.geneInfo).toBe(null) @@ -40,6 +47,7 @@ describe.concurrent('geneInfo Store', () => { }) it('should load data', async () => { + // arrange: const store = useGeneInfoStore() fetchMocker.mockResponse((req) => { if (req.url.includes('info')) { @@ -52,8 +60,11 @@ describe.concurrent('geneInfo Store', () => { return Promise.resolve(JSON.stringify({ status: 400 })) } }) + + // act: await store.loadData('HGNC:1100', 'GRCh37') + // assert: expect(store.storeState).toBe(StoreState.Active) expect(store.hgncId).toBe('HGNC:1100') expect(store.geneInfo).toEqual({ gene: 'info' }) @@ -62,6 +73,7 @@ describe.concurrent('geneInfo Store', () => { }) it('should fail to load data with invalid request to gene info', async () => { + // arrange: // Disable error logging vi.spyOn(console, 'error').mockImplementation(() => {}) const store = useGeneInfoStore() @@ -76,8 +88,11 @@ describe.concurrent('geneInfo Store', () => { return Promise.resolve(JSON.stringify({ status: 400 })) } }) + + // act: await store.loadData('invalid', 'invalid') + // assert: expect(store.storeState).toBe(StoreState.Error) expect(store.hgncId).toBe(null) expect(store.geneInfo).toBe(null) @@ -86,6 +101,7 @@ describe.concurrent('geneInfo Store', () => { }) it('should fail to load data with invalid request to clinvar info', async () => { + // arrange: // Disable error logging vi.spyOn(console, 'error').mockImplementation(() => {}) const store = useGeneInfoStore() @@ -100,8 +116,11 @@ describe.concurrent('geneInfo Store', () => { return Promise.resolve(JSON.stringify({ status: 400 })) } }) + + // act: await store.loadData('invalid', 'invalid') + // assert: expect(store.storeState).toBe(StoreState.Error) expect(store.hgncId).toBe(null) expect(store.geneInfo).toBe(null) @@ -110,19 +129,24 @@ describe.concurrent('geneInfo Store', () => { }) it('should not load data if gene symbol is the same', async () => { + // arrange: const store = useGeneInfoStore() fetchMocker.mockResponse(JSON.stringify({ genes: { 'HGNC:1100': { gene: 'info' } } })) + // act: await store.loadData('HGNC:1100', 'GRCh37') + // assert: expect(store.storeState).toBe(StoreState.Active) expect(store.hgncId).toBe('HGNC:1100') expect(store.geneInfo).toEqual({ gene: 'info' }) expect(store.geneClinvar).toEqual({ gene: 'info' }) expect(store.hgncId).toBe('HGNC:1100') + // act2: await store.loadData('HGNC:1100', 'GRCh37') + // assert2: expect(fetchMocker.mock.calls.length).toBe(4) }) }) diff --git a/frontend/src/stores/__tests__/genesList.spec.ts b/frontend/src/stores/__tests__/genesList.spec.ts index 2b400e2f..15f4526f 100644 --- a/frontend/src/stores/__tests__/genesList.spec.ts +++ b/frontend/src/stores/__tests__/genesList.spec.ts @@ -7,6 +7,7 @@ import { StoreState } from '../misc' const fetchMocker = createFetchMock(vi) +// Test data const exampleGenesList = { genes: [ { @@ -39,8 +40,12 @@ describe.concurrent('geneInfo Store', () => { }) it('should have initial state', () => { + // arrange: const store = useGenesListStore() + // act: nothing to do + + // assert: expect(store.storeState).toBe(StoreState.Initial) expect(store.query).toBe(null) expect(store.genesList).toBe(null) @@ -48,50 +53,62 @@ describe.concurrent('geneInfo Store', () => { }) it('should clear state', () => { + // arrange: const store = useGenesListStore() store.storeState = StoreState.Active store.query = 'q=BRCA1&fields=symbol' store.genesList = JSON.parse(JSON.stringify({ gene: 'info' })) + // act: store.clearData() + // assert: expect(store.storeState).toBe(StoreState.Initial) expect(store.query).toBe(null) expect(store.genesList).toBe(null) }) it('should load data', async () => { - const store = useGenesListStore() + // arrange: fetchMocker.mockResponse(JSON.stringify(exampleGenesList)) + const store = useGenesListStore() + // act: await store.loadData({ q: 'EMP', fields: 'hgnc_id,ensembl_gene_id,ncbi_gene_id,symbol' }) + // assert: expect(store.storeState).toBe(StoreState.Active) expect(store.query).toBe('q=EMP&fields=hgnc_id,ensembl_gene_id,ncbi_gene_id,symbol') expect(store.genesList).toEqual(exampleGenesList.genes) }) it('should fail to load data with invalid request', async () => { + // arrange: // Disable error logging vi.spyOn(console, 'error').mockImplementation(() => {}) - const store = useGenesListStore() fetchMocker.mockResponseOnce(JSON.stringify({ foo: 'bar' }), { status: 400 }) + const store = useGenesListStore() + // act: await store.loadData({ q: 'XXX', fields: 'hgnc_id,ensembl_gene_id,ncbi_gene_id,symbol' }) + // assert: expect(store.storeState).toBe(StoreState.Error) expect(store.query).toBe(null) expect(store.genesList).toBe(null) }) it('should fail to load data with invalid fetchGenes response', async () => { + // arrange: // Disable error logging vi.spyOn(console, 'error').mockImplementation(() => {}) - const store = useGenesListStore() fetchMocker.mockResponseOnce(JSON.stringify({ genes: [] })) + const store = useGenesListStore() + // act: await store.loadData({ q: 'EMP', fields: 'hgnc_id,ensembl_gene_id,ncbi_gene_id,symbol' }) + // assert: expect(console.error).toHaveBeenCalled() expect(console.error).toHaveBeenCalledWith( 'There was an error while searching for genes.', @@ -103,76 +120,89 @@ describe.concurrent('geneInfo Store', () => { }) it('should not load data if gene symbol is the same', async () => { - const store = useGenesListStore() + // arrange: fetchMocker.mockResponse(JSON.stringify(exampleGenesList)) + const store = useGenesListStore() + // act: await store.loadData({ q: 'EMP', fields: 'hgnc_id,ensembl_gene_id,ncbi_gene_id,symbol' }) + // assert: expect(store.storeState).toBe(StoreState.Active) expect(store.query).toBe('q=EMP&fields=hgnc_id,ensembl_gene_id,ncbi_gene_id,symbol') expect(store.genesList).toEqual(exampleGenesList.genes) + // act2: await store.loadData({ q: 'EMP', fields: 'hgnc_id,ensembl_gene_id,ncbi_gene_id,symbol' }) + // assert2: expect(fetchMocker.mock.calls.length).toBe(1) }) it('should redirect if the searchTerm matched symbol', async () => { - const store = useGenesListStore() + // arrange: fetchMocker.mockResponse(JSON.stringify(exampleGenesList)) + const store = useGenesListStore() + // act: await store.loadData({ q: 'EMP1', fields: 'hgnc_id,ensembl_gene_id,ncbi_gene_id,symbol' }) + // assert: expect(store.storeState).toBe(StoreState.Redirect) expect(store.redirectHgncId).toBe('HGNC:3333') expect(store.query).toBe('q=EMP1&fields=hgnc_id,ensembl_gene_id,ncbi_gene_id,symbol') expect(store.genesList).toEqual(exampleGenesList.genes) - expect(fetchMocker.mock.calls.length).toBe(1) }) it('should redirect if the searchTerm matched hgnc_id', async () => { - const store = useGenesListStore() + // arrange: fetchMocker.mockResponse(JSON.stringify(exampleGenesList)) + const store = useGenesListStore() + // act: await store.loadData({ q: 'HGNC:3333', fields: 'hgnc_id,ensembl_gene_id,ncbi_gene_id,symbol' }) + // assert: expect(store.storeState).toBe(StoreState.Redirect) expect(store.redirectHgncId).toBe('HGNC:3333') expect(store.query).toBe('q=HGNC:3333&fields=hgnc_id,ensembl_gene_id,ncbi_gene_id,symbol') expect(store.genesList).toEqual(exampleGenesList.genes) - expect(fetchMocker.mock.calls.length).toBe(1) }) it('should redirect if the searchTerm matched ensembl_gene_id', async () => { - const store = useGenesListStore() + // arrange: fetchMocker.mockResponse(JSON.stringify(exampleGenesList)) + const store = useGenesListStore() + // act: await store.loadData({ q: 'ENSG00000134531', fields: 'hgnc_id,ensembl_gene_id,ncbi_gene_id,symbol' }) + // assert: expect(store.storeState).toBe(StoreState.Redirect) expect(store.redirectHgncId).toBe('HGNC:3333') expect(store.query).toBe('q=ENSG00000134531&fields=hgnc_id,ensembl_gene_id,ncbi_gene_id,symbol') expect(store.genesList).toEqual(exampleGenesList.genes) - expect(fetchMocker.mock.calls.length).toBe(1) }) it('should redirect if the searchTerm matched ncbi_gene_id', async () => { - const store = useGenesListStore() + // arrange: fetchMocker.mockResponse(JSON.stringify(exampleGenesList)) + const store = useGenesListStore() + // act: await store.loadData({ q: '2012', fields: 'hgnc_id,ensembl_gene_id,ncbi_gene_id,symbol' }) + // assert: expect(store.storeState).toBe(StoreState.Redirect) expect(store.redirectHgncId).toBe('HGNC:3333') expect(store.query).toBe('q=2012&fields=hgnc_id,ensembl_gene_id,ncbi_gene_id,symbol') expect(store.genesList).toEqual(exampleGenesList.genes) - expect(fetchMocker.mock.calls.length).toBe(1) }) }) diff --git a/frontend/src/stores/__tests__/misc.spec.ts b/frontend/src/stores/__tests__/misc.spec.ts index c75eaf80..0cb1d789 100644 --- a/frontend/src/stores/__tests__/misc.spec.ts +++ b/frontend/src/stores/__tests__/misc.spec.ts @@ -14,30 +14,40 @@ describe.concurrent('miscInfo Store', () => { }) it('should have initial state', () => { + // arrange: const store = useMiscStore() + // act: nothing to do + + // assert: expect(store.storeState).toBe(StoreState.Initial) expect(store.appVersion).toBe(null) }) it('should load data', async () => { - const store = useMiscStore() + // arrange: fetchMocker.mockResponseOnce('v0.0.0') + const store = useMiscStore() + // act: await store.initialize() + // assert: expect(store.storeState).toBe(StoreState.Active) expect(store.appVersion).toBe('v0.0.0') }) it('should handle error', async () => { + // arrange: // Disable error logging vi.spyOn(console, 'error').mockImplementation(() => {}) - const store = useMiscStore() fetchMocker.mockRejectOnce(new Error('error')) + const store = useMiscStore() + // act: await store.initialize() + // assert: expect(store.storeState).toBe(StoreState.Error) expect(store.appVersion).toBe(null) }) diff --git a/frontend/src/stores/__tests__/seqvarAcmgRating.spec.ts b/frontend/src/stores/__tests__/seqvarAcmgRating.spec.ts index 5fd7a4a3..bab8f68b 100644 --- a/frontend/src/stores/__tests__/seqvarAcmgRating.spec.ts +++ b/frontend/src/stores/__tests__/seqvarAcmgRating.spec.ts @@ -11,6 +11,7 @@ import { useSeqvarAcmgRatingStore } from '../seqvarAcmgRating' const fetchMocker = createFetchMock(vi) +// Test data const seqvarInfo: Seqvar = { genomeBuild: 'grch37', chrom: '17', @@ -57,8 +58,12 @@ describe.concurrent('geneInfo Store', () => { }) it('should have initial state', () => { + // arrange: const store = useSeqvarAcmgRatingStore() + // act: nothing to do + + // assert: expect(store.storeState).toBe(StoreState.Initial) expect(store.acmgRating).toStrictEqual(new MultiSourceAcmgCriteriaState()) expect(store.seqvar).toBe(null) @@ -66,20 +71,23 @@ describe.concurrent('geneInfo Store', () => { }) it('should clear state', () => { + // arrange: const store = useSeqvarAcmgRatingStore() store.storeState = StoreState.Active store.acmgRating = JSON.parse(JSON.stringify({ acmg: 'rating' })) store.seqvar = structuredClone(seqvarInfo) + // act: store.clearData() + // assert: expect(store.storeState).toBe(StoreState.Initial) expect(store.acmgRating).toStrictEqual(new MultiSourceAcmgCriteriaState()) expect(store.seqvar).toBe(null) }) it('should correctly retrieve data for InterVar and Server', async () => { - const store = useSeqvarAcmgRatingStore() + // arrange: fetchMocker.mockResponse((req) => { if (req.url.includes('remote/acmg')) { return Promise.resolve(JSON.stringify(ExampleInterVarResponse)) @@ -88,9 +96,12 @@ describe.concurrent('geneInfo Store', () => { } return Promise.resolve(JSON.stringify({ status: 400 })) }) + const store = useSeqvarAcmgRatingStore() + // act: await store.fetchAcmgRating(structuredClone(seqvarInfo)) + // assert: expect(store.storeState).toBe(StoreState.Active) const expectedAcmgRating = new MultiSourceAcmgCriteriaState() for (const [key, value] of Object.entries(ExampleInterVarResponse)) { @@ -122,22 +133,25 @@ describe.concurrent('geneInfo Store', () => { }) it('should fail to load data with invalid request', async () => { + // arrange: // Disable error logging vi.spyOn(console, 'error').mockImplementation(() => {}) - const store = useSeqvarAcmgRatingStore() fetchMocker.mockResponseOnce(JSON.stringify({ foo: 'bar' }), { status: 400 }) + const store = useSeqvarAcmgRatingStore() + // act: await expect( async () => await store.fetchAcmgRating(structuredClone(seqvarInfo)) ).rejects.toThrow() + // assert: expect(store.storeState).toBe(StoreState.Error) expect(store.acmgRating).toStrictEqual(new MultiSourceAcmgCriteriaState()) expect(store.seqvar).toBe(null) }) it('should not load data if small variant is the same', async () => { - const store = useSeqvarAcmgRatingStore() + // arrange: fetchMocker.mockResponse((req) => { if (req.url.includes('remote/acmg')) { return Promise.resolve(JSON.stringify(ExampleInterVarResponse)) @@ -146,8 +160,12 @@ describe.concurrent('geneInfo Store', () => { } return Promise.resolve(JSON.stringify({ status: 400 })) }) + const store = useSeqvarAcmgRatingStore() + + // act: await store.fetchAcmgRating(structuredClone(seqvarInfo)) + // assert: expect(store.storeState).toBe(StoreState.Active) const expectedAcmgRating = new MultiSourceAcmgCriteriaState() for (const [key, value] of Object.entries(ExampleInterVarResponse)) { @@ -177,8 +195,10 @@ describe.concurrent('geneInfo Store', () => { expect(store.acmgRating).toStrictEqual(expectedAcmgRating) expect(store.seqvar).toStrictEqual(seqvarInfo) + // act2: await store.fetchAcmgRating(structuredClone(seqvarInfo)) + // assert2: expect(fetchMocker.mock.calls.length).toBe(2) }) }) diff --git a/frontend/src/stores/__tests__/strucvarAcmgRating.spec.ts b/frontend/src/stores/__tests__/strucvarAcmgRating.spec.ts index 555dfe75..52ba66a6 100644 --- a/frontend/src/stores/__tests__/strucvarAcmgRating.spec.ts +++ b/frontend/src/stores/__tests__/strucvarAcmgRating.spec.ts @@ -17,6 +17,7 @@ import { useStrucvarAcmgRatingStore } from '../strucvarAcmgRating' const fetchMocker = createFetchMock(vi) +// Test data const strucvarInfo: Strucvar = { genomeBuild: 'grch37', svType: 'DEL', @@ -26,46 +27,6 @@ const strucvarInfo: Strucvar = { userRepr: 'DEL-grch37-17-41176312-41277500' } -// const svRecord = { -// svType: 'DEL', -// chromosome: 'chr17', -// start: '41176312', -// end: '41277500', -// release: 'grch37', -// result: [ -// { -// hgnc_id: 'HGNC:18315', -// transcript_effects: [ -// 'transcript_variant', -// 'exon_variant', -// 'splice_region_variant', -// 'intron_variant', -// 'upstream_variant', -// 'downstream_variant' -// ] -// }, -// { -// hgnc_id: 'HGNC:20691', -// transcript_effects: ['upstream_variant'] -// }, -// { -// hgnc_id: 'HGNC:1100', -// transcript_effects: [ -// 'transcript_variant', -// 'exon_variant', -// 'splice_region_variant', -// 'intron_variant', -// 'upstream_variant', -// 'downstream_variant' -// ] -// }, -// { -// hgnc_id: 'HGNC:16919', -// transcript_effects: ['upstream_variant'] -// } -// ] -// } - const ExampleAutoCNVResponse = { job: { result: { @@ -98,32 +59,42 @@ describe.concurrent('geneInfo Store', () => { }) it('should have initial state', () => { + // arrange: const store = useStrucvarAcmgRatingStore() + // act: nothing to do + + // assert: expect(store.storeState).toBe(StoreState.Initial) expect(store.acmgRating).toStrictEqual(new MultiSourceAcmgCriteriaCNVState('DEL')) expect(store.strucvar).toBe(undefined) }) it('should clear state', () => { + // arrange: const store = useStrucvarAcmgRatingStore() store.storeState = StoreState.Active store.acmgRating = JSON.parse(JSON.stringify({ acmg: 'rating' })) store.strucvar = structuredClone(strucvarInfo) + // act: store.clearData() + // assert: expect(store.storeState).toBe(StoreState.Initial) expect(store.acmgRating).toStrictEqual(new MultiSourceAcmgCriteriaCNVState('DEL')) expect(store.strucvar).toBe(undefined) }) it('should correctly retrieve data', async () => { - const store = useStrucvarAcmgRatingStore() + // arrange: fetchMocker.mockResponseOnce(JSON.stringify(ExampleAutoCNVResponse)) + const store = useStrucvarAcmgRatingStore() + // act: await store.fetchAcmgRating(structuredClone(strucvarInfo)) + // assert: expect(store.storeState).toBe(StoreState.Active) const expectedAcmgRating = new MultiSourceAcmgCriteriaCNVState('DEL') for (const criteria of ACMG_CRITERIA_CNV_LOSS) { @@ -168,23 +139,30 @@ describe.concurrent('geneInfo Store', () => { }) it('should fail to load data with invalid request', async () => { + // arrange: // Disable error logging vi.spyOn(console, 'error').mockImplementation(() => {}) - const store = useStrucvarAcmgRatingStore() fetchMocker.mockResponseOnce(JSON.stringify({ foo: 'bar' }), { status: 400 }) + const store = useStrucvarAcmgRatingStore() + // act: await store.fetchAcmgRating(structuredClone(strucvarInfo)) + // assert: expect(store.storeState).toBe(StoreState.Error) expect(store.acmgRating).toStrictEqual(new MultiSourceAcmgCriteriaCNVState('DEL')) expect(store.strucvar).toBe(undefined) }) it('should not load data if structure variant is the same', async () => { - const store = useStrucvarAcmgRatingStore() + // arrange: fetchMocker.mockResponse(JSON.stringify(ExampleAutoCNVResponse)) + const store = useStrucvarAcmgRatingStore() + + // act: await store.fetchAcmgRating(structuredClone(strucvarInfo)) + // assert: expect(store.storeState).toBe(StoreState.Active) const expectedAcmgRating = new MultiSourceAcmgCriteriaCNVState('DEL') for (const criteria of ACMG_CRITERIA_CNV_LOSS) { @@ -227,8 +205,10 @@ describe.concurrent('geneInfo Store', () => { expect(store.acmgRating).toStrictEqual(expectedAcmgRating) expect(store.strucvar).toStrictEqual(structuredClone(strucvarInfo)) + // act2: await store.fetchAcmgRating(structuredClone(strucvarInfo)) + // assert2: expect(fetchMocker.mock.calls.length).toBe(1) }) }) diff --git a/frontend/src/stores/__tests__/strucvarInfo.spec.ts b/frontend/src/stores/__tests__/strucvarInfo.spec.ts index 6c565314..5da90a31 100644 --- a/frontend/src/stores/__tests__/strucvarInfo.spec.ts +++ b/frontend/src/stores/__tests__/strucvarInfo.spec.ts @@ -9,6 +9,7 @@ import { useStrucvarInfoStore } from '@/stores/strucvarInfo' const fetchMocker = createFetchMock(vi) +// Test data const strucvarInfo: Strucvar = { genomeBuild: 'grch37', svType: 'DEL', @@ -26,28 +27,35 @@ describe.concurrent('svInfo Store', () => { }) it('should have initial state', () => { + // arrange: const store = useStrucvarInfoStore() + // act: nothing to do + + // assert: expect(store.storeState).toBe(StoreState.Initial) expect(store.strucvar).toBe(undefined) expect(store.genesInfos).toStrictEqual(undefined) }) it('should clear state', () => { + // arrange: const store = useStrucvarInfoStore() store.storeState = StoreState.Active store.strucvar = structuredClone(strucvarInfo) store.genesInfos = JSON.parse(JSON.stringify([geneInfo['genes']['HGNC:1100']])) + // act: store.clearData() + // assert: expect(store.storeState).toBe(StoreState.Initial) expect(store.strucvar).toBe(undefined) expect(store.genesInfos).toStrictEqual(undefined) }) it('should load data', async () => { - const store = useStrucvarInfoStore() + // arrange: fetchMocker.mockResponse((req) => { if (req.url.includes('csq')) { return Promise.resolve(JSON.stringify({ result: [{ hgnc_id: 'HGNC:1100' }] })) @@ -55,19 +63,22 @@ describe.concurrent('svInfo Store', () => { return Promise.resolve(JSON.stringify(geneInfo)) } }) + const store = useStrucvarInfoStore() + + // act: await store.loadData(structuredClone(strucvarInfo)) + // assert: expect(store.storeState).toBe(StoreState.Active) expect(store.strucvar).toStrictEqual(strucvarInfo) expect(store.genesInfos).toStrictEqual([geneInfo['genes']['HGNC:1100']]) }) it('should correctly handle errors', async () => { + // arrange: // Disable console.error const spy = vi.spyOn(console, 'error') spy.mockImplementation(() => {}) - - const store = useStrucvarInfoStore() fetchMocker.mockResponse((req) => { if (req.url.includes('csq')) { return Promise.resolve(JSON.stringify({ status: 400 })) @@ -75,8 +86,12 @@ describe.concurrent('svInfo Store', () => { return Promise.resolve(JSON.stringify({ status: 400 })) } }) + const store = useStrucvarInfoStore() + + // act: await store.loadData(structuredClone(strucvarInfo)) + // assert: expect(store.storeState).toBe(StoreState.Error) expect(store.strucvar).toBe(undefined) expect(store.genesInfos).toStrictEqual(undefined) diff --git a/frontend/src/stores/__tests__/terms.spec.ts b/frontend/src/stores/__tests__/terms.spec.ts index 22878eed..b6adf387 100644 --- a/frontend/src/stores/__tests__/terms.spec.ts +++ b/frontend/src/stores/__tests__/terms.spec.ts @@ -15,88 +15,113 @@ describe.concurrent('Terms Store', () => { }) it('initial state', () => { + // arrange: const store = useTermsStore() + // act: nothing to do + + // assert: expect(store.storeState).toBe(StoreState.Initial) expect(store.hpoTerms).toStrictEqual([]) expect(store.omimTerms).toStrictEqual([]) }) it('clears data', () => { + // arrange: const store = useTermsStore() store.hpoTerms = [{ term_id: 'HP:0000118', name: 'Phenotypic abnormality' }] + // act: store.clearData() + // assert: expect(store.hpoTerms).toStrictEqual([]) }) it('fetches HPO terms by name', async () => { + // arrange: const mockHpoTerms = [{ term_id: 'HP:0000118', name: 'Phenotypic abnormality' }] fetchMocker.mockResponseOnce(JSON.stringify({ result: mockHpoTerms })) - const store = useTermsStore() + + // act: await store.fetchHpoTerms('Phenotypic abnormality') + // assert: expect(store.storeState).toBe(StoreState.Active) expect(store.hpoTerms).toEqual(mockHpoTerms) }) it('fetches HPO term by ID', async () => { + // arrange: const mockHpoTerms = [{ term_id: 'HP:0000118', name: 'Phenotypic abnormality' }] fetchMocker.mockResponseOnce(JSON.stringify({ result: mockHpoTerms })) + // act: const store = useTermsStore() await store.fetchHpoTerms('HP:0000118') + // assert: expect(store.storeState).toBe(StoreState.Active) expect(store.hpoTerms).toEqual(mockHpoTerms) }) it('handles error when fetching HPO terms', async () => { + // arrange: // Disable error logging vi.spyOn(console, 'error').mockImplementation(() => {}) fetchMocker.mockResponseOnce(JSON.stringify({}), { status: 500 }) - const store = useTermsStore() + + // act: await store.fetchHpoTerms('InvalidQuery') + // assert: expect(store.storeState).toBe(StoreState.Error) expect(store.hpoTerms).toStrictEqual([]) }) it('fetches OMIM terms by name', async () => { + // arrange: // Disable error logging vi.spyOn(console, 'error').mockImplementation(() => {}) const mockOmimTerms = [{ term_id: 'OMIM:123456', name: 'Example Disease' }] fetchMocker.mockResponseOnce(JSON.stringify({ result: mockOmimTerms })) - const store = useTermsStore() + + // act: await store.fetchOmimTerms('Example Disease') + // assert: expect(store.storeState).toBe(StoreState.Active) expect(store.omimTerms).toEqual(mockOmimTerms) }) it('fetches OMIM term by ID', async () => { + // arrange: const mockOmimTerm = { term_id: 'OMIM:123456', name: 'Example Disease' } fetchMocker.mockResponseOnce(JSON.stringify({ result: mockOmimTerm })) - const store = useTermsStore() + + // act: await store.fetchOmimTerms('OMIM:123456') + // assert: expect(store.storeState).toBe(StoreState.Active) expect(store.omimTerms).toEqual([mockOmimTerm]) }) it('handles error when fetching OMIM terms', async () => { + // arrange: // Disable error logging vi.spyOn(console, 'error').mockImplementation(() => {}) fetchMocker.mockResponseOnce(JSON.stringify({}), { status: 500 }) - const store = useTermsStore() + + // act: await store.fetchOmimTerms('InvalidQuery') + // assert: expect(store.storeState).toBe(StoreState.Error) expect(store.omimTerms).toStrictEqual([]) }) diff --git a/frontend/src/stores/__tests__/user.spec.ts b/frontend/src/stores/__tests__/user.spec.ts index e51c26d5..ed5131aa 100644 --- a/frontend/src/stores/__tests__/user.spec.ts +++ b/frontend/src/stores/__tests__/user.spec.ts @@ -15,14 +15,19 @@ describe.concurrent('User Store', () => { }) it('should have initial state', () => { + // arrange: const store = useUserStore() + // act: nothing to do + + // assert: expect(store.storeState).toBe(StoreState.Initial) expect(store.currentUser).toBe(undefined) expect(store.isAuthenticated).toBe(false) }) it('should load current user', async () => { + // arrange: const mockUser: UserData = { id: '1', email: 'test@example.com', @@ -31,40 +36,48 @@ describe.concurrent('User Store', () => { is_verified: true, oauth_accounts: [] } - - const store = useUserStore() fetchMocker.mockResponse(JSON.stringify(mockUser)) + const store = useUserStore() + // act: await store.loadCurrentUser() + // assert: expect(store.storeState).toBe(StoreState.Active) expect(store.currentUser).toEqual(mockUser) expect(store.isAuthenticated).toBe(true) }) it('should handle unauthenticated error when loading current user', async () => { - const store = useUserStore() + // arrange: fetchMocker.mockResponse(JSON.stringify({ detail: 'Unauthenticated' }), { status: 401 }) + const store = useUserStore() + // act: await store.loadCurrentUser() + // assert: expect(store.storeState).toBe(StoreState.Active) expect(store.currentUser).toBe(null) expect(store.isAuthenticated).toBe(false) }) it('should handle other errors when loading current user', async () => { - const store = useUserStore() + // arrange: fetchMocker.mockResponse(JSON.stringify({ detail: 'Internal Server Error' }), { status: 500 }) + const store = useUserStore() + // act: await store.loadCurrentUser() + // assert: expect(store.storeState).toBe(StoreState.Active) expect(store.currentUser).toBe(null) expect(store.isAuthenticated).toBe(false) }) it('should initialize store', async () => { + // arrange: const mockUser: UserData = { id: '1', email: 'test@example.com', @@ -73,18 +86,20 @@ describe.concurrent('User Store', () => { is_verified: true, oauth_accounts: [] } - - const store = useUserStore() fetchMocker.mockResponse(JSON.stringify(mockUser)) + const store = useUserStore() + // act: await store.initialize() + // assert: expect(store.storeState).toBe(StoreState.Active) expect(store.currentUser).toEqual(mockUser) expect(store.isAuthenticated).toBe(true) }) it('should not initialize store twice', async () => { + // arrange: const mockUser: UserData = { id: '1', email: 'test@example.com', @@ -93,15 +108,15 @@ describe.concurrent('User Store', () => { is_verified: true, oauth_accounts: [] } - - const store = useUserStore() fetchMocker.mockResponse(JSON.stringify(mockUser)) + const store = useUserStore() + // act: await store.initialize() const initialState = { ...store } - await store.initialize() + // assert: expect(store.storeState).toBe(StoreState.Active) expect(store.currentUser).toEqual(mockUser) expect(store.isAuthenticated).toBe(true) diff --git a/frontend/src/stores/__tests__/variantInfo.spec.ts b/frontend/src/stores/__tests__/variantInfo.spec.ts index 24e37714..db203ea8 100644 --- a/frontend/src/stores/__tests__/variantInfo.spec.ts +++ b/frontend/src/stores/__tests__/variantInfo.spec.ts @@ -13,6 +13,7 @@ import { useSeqvarInfoStore } from '../seqvarInfo' const fetchMocker = createFetchMock(vi) +// Test data const seqvarInfo: Seqvar = { genomeBuild: 'grch37', chrom: '17', @@ -30,8 +31,12 @@ describe.concurrent('geneInfo Store', () => { }) it('should have initial state', () => { + // arrange: const store = useSeqvarInfoStore() + // act: nothing to do + + // assert: expect(store.storeState).toBe(StoreState.Initial) expect(store.seqvar).toBe(undefined) expect(store.varAnnos).toBe(null) @@ -41,6 +46,7 @@ describe.concurrent('geneInfo Store', () => { }) it('should clear state', () => { + // arrange: const store = useSeqvarInfoStore() store.storeState = StoreState.Active store.seqvar = structuredClone(seqvarInfo) @@ -48,8 +54,10 @@ describe.concurrent('geneInfo Store', () => { store.geneInfo = JSON.parse(JSON.stringify(BRCA1GeneInfo)) store.txCsq = JSON.parse(JSON.stringify(BRCA1TxInfo)) + // act: store.clearData() + // assert: expect(store.storeState).toBe(StoreState.Initial) expect(store.seqvar).toBe(undefined) expect(store.varAnnos).toBe(null) @@ -58,7 +66,7 @@ describe.concurrent('geneInfo Store', () => { }) it('should load data', async () => { - const store = useSeqvarInfoStore() + // arrange: fetchMocker.mockResponse((req) => { if (req.url.includes('annos/variant')) { return Promise.resolve(JSON.stringify(BRCA1VariantInfo)) @@ -72,9 +80,12 @@ describe.concurrent('geneInfo Store', () => { return Promise.resolve(JSON.stringify({ status: 400 })) } }) + const store = useSeqvarInfoStore() + // act: await store.loadData(structuredClone(seqvarInfo)) + // assert: expect(store.storeState).toBe(StoreState.Active) expect(store.seqvar).toStrictEqual(seqvarInfo) expect(store.varAnnos).toEqual(BRCA1VariantInfo.result) @@ -83,13 +94,16 @@ describe.concurrent('geneInfo Store', () => { }) it('should fail to load data with invalid request', async () => { + // arrange: // Disable error logging vi.spyOn(console, 'error').mockImplementation(() => {}) - const store = useSeqvarInfoStore() fetchMocker.mockResponseOnce(JSON.stringify({ foo: 'bar' }), { status: 400 }) + const store = useSeqvarInfoStore() + // act: await store.loadData(structuredClone(seqvarInfo)) + // assert: expect(store.storeState).toBe(StoreState.Error) expect(store.seqvar).toBe(undefined) expect(store.varAnnos).toBe(null) @@ -98,9 +112,9 @@ describe.concurrent('geneInfo Store', () => { }) it('should handle loading data with invalid fetchVariantInfo response', async () => { + // arrange: // Disable error logging vi.spyOn(console, 'error').mockImplementation(() => {}) - const store = useSeqvarInfoStore() fetchMocker.mockResponse((req) => { if (req.url.includes('annos/variant')) { return Promise.resolve( @@ -116,9 +130,12 @@ describe.concurrent('geneInfo Store', () => { return Promise.resolve(JSON.stringify({ status: 400 })) } }) + const store = useSeqvarInfoStore() + // act: await store.loadData(structuredClone(seqvarInfo)) + // assert: expect(store.storeState).toBe(StoreState.Active) expect(store.seqvar).toStrictEqual(seqvarInfo) expect(store.varAnnos).toStrictEqual({ @@ -133,9 +150,9 @@ describe.concurrent('geneInfo Store', () => { }) it('should handle loading data with invalid retrieveSeqvarsCsq response', async () => { + // arrange: // Disable error logging vi.spyOn(console, 'error').mockImplementation(() => {}) - const store = useSeqvarInfoStore() fetchMocker.mockResponse((req) => { if (req.url.includes('annos/variant')) { return Promise.resolve(JSON.stringify(BRCA1VariantInfo)) @@ -149,21 +166,23 @@ describe.concurrent('geneInfo Store', () => { return Promise.resolve(JSON.stringify({ status: 400 })) } }) + const store = useSeqvarInfoStore() + // act: await store.loadData(structuredClone(seqvarInfo)) + // assert: expect(store.storeState).toBe(StoreState.Active) expect(store.seqvar).toStrictEqual(seqvarInfo) - expect(store.varAnnos).toStrictEqual(BRCA1VariantInfo.result) expect(store.geneInfo).toEqual(null) expect(store.txCsq).toStrictEqual([]) }) it('should fail to load data with invalid fetchGeneInfo response', async () => { + // arrange: // Disable error logging vi.spyOn(console, 'error').mockImplementation(() => {}) - const store = useSeqvarInfoStore() fetchMocker.mockResponse((req) => { if (req.url.includes('annos/variant')) { return Promise.resolve(JSON.stringify(BRCA1VariantInfo)) @@ -177,9 +196,12 @@ describe.concurrent('geneInfo Store', () => { return Promise.resolve(JSON.stringify({ status: 400 })) } }) + const store = useSeqvarInfoStore() + // act: await store.loadData(structuredClone(seqvarInfo)) + // assert: expect(console.error).toHaveBeenCalled() expect(console.error).toHaveBeenCalledWith( 'There was an error loading the variant data.', @@ -193,7 +215,7 @@ describe.concurrent('geneInfo Store', () => { }) it('should not load data if variant is the same', async () => { - const store = useSeqvarInfoStore() + // arrange: fetchMocker.mockResponse((req) => { if (req.url.includes('annos/variant')) { return Promise.resolve(JSON.stringify(BRCA1VariantInfo)) @@ -207,17 +229,22 @@ describe.concurrent('geneInfo Store', () => { return Promise.resolve(JSON.stringify({ status: 400 })) } }) + const store = useSeqvarInfoStore() + // act: await store.loadData(structuredClone(seqvarInfo)) + // assert: expect(store.storeState).toBe(StoreState.Active) expect(store.seqvar).toStrictEqual(seqvarInfo) expect(store.varAnnos).toEqual(BRCA1VariantInfo.result) expect(store.geneInfo).toEqual(BRCA1GeneInfo.genes['HGNC:1100']) expect(store.txCsq).toEqual(BRCA1TxInfo.result) + // act2: await store.loadData(structuredClone(seqvarInfo)) + // assert2: expect(fetchMocker.mock.calls.length).toBe(5) }) })