From fbe0869bd90d82cf5d0d673a8b5325a5a1329cd7 Mon Sep 17 00:00:00 2001 From: Felix Hennig Date: Thu, 9 Jul 2026 14:21:50 +0200 Subject: [PATCH 1/3] test(wasap): add tests for fetchWasapPageData covering all 5 fetch modes Also converts WasapAnalysisMode, SignatureType, ExcludeSetName to const-object pattern (matching VARIANT_TIME_FRAME), adds SEQUENCE_TYPE for the external SequenceType, exports fetchWasapPageData for direct testing, and adds BackendRouteMocker.mockGetCollection to routeMocker.ts. Co-Authored-By: Claude Sonnet 4.6 --- website/routeMocker.ts | 10 +- .../views/wasap/useWasapPageData.spec.ts | 525 ++++++++++++++++++ .../views/wasap/useWasapPageData.ts | 2 +- .../components/views/wasap/wasapPageConfig.ts | 29 +- 4 files changed, 561 insertions(+), 5 deletions(-) create mode 100644 website/src/components/views/wasap/useWasapPageData.spec.ts diff --git a/website/routeMocker.ts b/website/routeMocker.ts index 29b58c6d3..7842b3529 100644 --- a/website/routeMocker.ts +++ b/website/routeMocker.ts @@ -8,7 +8,7 @@ import { type OrganismsConfig } from './src/config'; import type { CollectionRaw } from './src/covspectrum/types.ts'; import type { LapisInfo } from './src/lapis/getLastUpdatedDate.ts'; import type { ParsedQueryResult, ParseQueryRequest } from './src/lapis/parseQuery.ts'; -import type { CollectionSummary } from './src/types/Collection.ts'; +import type { Collection, CollectionSummary } from './src/types/Collection.ts'; import type { ProblemDetail } from './src/types/ProblemDetail.ts'; import type { SubscriptionPutRequest, @@ -238,6 +238,14 @@ export class BackendRouteMocker { ); } + mockGetCollection(id: string, response: Collection, statusCode = 200) { + this.workerOrServer.use( + http.get(`${DUMMY_BACKEND_URL}/collections/${id}`, () => { + return new Response(JSON.stringify(response), { status: statusCode }); + }), + ); + } + mockGetCollectionTags(tags: string[] = [], statusCode = 200) { this.workerOrServer.use( http.get(`${DUMMY_BACKEND_URL}/collections/tags`, resolver([{ statusCode, response: { tags } }])), diff --git a/website/src/components/views/wasap/useWasapPageData.spec.ts b/website/src/components/views/wasap/useWasapPageData.spec.ts new file mode 100644 index 000000000..fbb51f803 --- /dev/null +++ b/website/src/components/views/wasap/useWasapPageData.spec.ts @@ -0,0 +1,525 @@ +import dayjs from 'dayjs'; +import { http } from 'msw'; +import { beforeEach, describe, expect, test, vi } from 'vitest'; + +import { fetchWasapPageData, getLapisFilterForTimeFrame } from './useWasapPageData.ts'; +import { + EXCLUDE_SET_NAME, + SEQUENCE_TYPE, + SIGNATURE_TYPE, + VARIANT_TIME_FRAME, + WASAP_ANALYSIS_MODE, + type WasapPageConfig, +} from './wasapPageConfig.ts'; +import { DUMMY_BACKEND_URL, DUMMY_LAPIS_URL } from '../../../../routeMocker.ts'; +import { astroApiRouteMocker, backendRouteMocker, lapisRouteMocker, testServer } from '../../../../vitest.setup.ts'; +import type { Collection } from '../../../types/Collection.ts'; + +vi.mock('../../../backendApi/backendService.ts', async (importOriginal) => { + const mod = await importOriginal(); + return { + ...mod, + getBackendServiceForClientside: () => new mod.BackendService(DUMMY_BACKEND_URL), + }; +}); + +const DUMMY_COV_SPECTRUM_URL = 'http://cov-spectrum.dummy/api/v2'; + +// these fields have no effect on data fetching, but need to be present to have a correct type. +const unusedBaseConfigFields = { + internalName: '', + name: '', + path: '', + description: '', + linkTemplate: { nucleotideMutation: '', aminoAcidMutation: '' }, + samplingDateField: '', + locationNameField: '', + defaultLocationName: '', + browseDataUrl: '', + browseDataDescription: '', +}; + +const baseConfigFields: WasapPageConfig = { + ...unusedBaseConfigFields, + lapisBaseUrl: DUMMY_LAPIS_URL, +}; + +describe('fetchWasapPageData', () => { + beforeEach(() => { + astroApiRouteMocker.mockLog(); + }); + + describe('manual mode', () => { + const config = { + ...baseConfigFields, + manualAnalysisModeEnabled: true as const, + filterDefaults: { manual: { mode: WASAP_ANALYSIS_MODE.manual, sequenceType: SEQUENCE_TYPE.nucleotide } }, + }; + + test('returns mutations when mode is enabled', async () => { + const result = await fetchWasapPageData( + config, + {}, + { + mode: WASAP_ANALYSIS_MODE.manual, + sequenceType: SEQUENCE_TYPE.nucleotide, + mutations: ['A123T', 'G456C'], + }, + ); + + expect(result).toEqual({ type: 'mutations', displayMutations: ['A123T', 'G456C'] }); + }); + + test('throws when mode is not enabled', async () => { + const disabledConfig = { ...baseConfigFields }; + + await expect( + fetchWasapPageData( + disabledConfig, + {}, + { mode: WASAP_ANALYSIS_MODE.manual, sequenceType: SEQUENCE_TYPE.nucleotide, mutations: [] }, + ), + ).rejects.toThrow("Cannot fetch data, 'manual' mode is not enabled."); + }); + }); + + describe('resistance mode', () => { + test('returns mutations for the given resistance set', async () => { + const result = await fetchWasapPageData( + baseConfigFields, + // eslint-disable-next-line @typescript-eslint/naming-convention + { Spike: ['S:E484K', 'S:N501Y'] }, + { mode: WASAP_ANALYSIS_MODE.resistance, sequenceType: SEQUENCE_TYPE.aminoAcid, resistanceSet: 'Spike' }, + ); + + expect(result).toEqual({ type: 'mutations', displayMutations: ['S:E484K', 'S:N501Y'] }); + }); + + test('returns empty array for unknown resistance set', async () => { + const result = await fetchWasapPageData( + baseConfigFields, + {}, + { + mode: WASAP_ANALYSIS_MODE.resistance, + sequenceType: SEQUENCE_TYPE.aminoAcid, + resistanceSet: 'Unknown', + }, + ); + + expect(result).toEqual({ type: 'mutations', displayMutations: [] }); + }); + }); + + describe('variant mode', () => { + const config = { + ...baseConfigFields, + variantAnalysisModeEnabled: true as const, + clinicalLapis: { + lapisBaseUrl: DUMMY_LAPIS_URL, + dateField: 'date', + lineageField: 'pangoLineage', + }, + filterDefaults: { + variant: { + mode: WASAP_ANALYSIS_MODE.variant, + signatureType: SIGNATURE_TYPE.computed, + sequenceType: SEQUENCE_TYPE.nucleotide, + variant: 'XEC', + minProportion: 0.8, + minCount: 15, + minJaccard: 0.3, + timeFrame: VARIANT_TIME_FRAME.all, + }, + }, + clinicalSequenceCountWarningThreshold: 100, + }; + + test('computed signature: fetches mutations from clinical LAPIS and annotates with jaccard scores', async () => { + // getMutationsForVariant makes 3 concurrent requests: + // (1) mutations with lineage filter, (2) all mutations, (3) total count for the lineage + lapisRouteMocker.mockPostNucleotideMutationsMulti([ + { + body: { pangoLineage: 'XEC', minProportion: 0.8 }, + response: { data: [{ mutation: 'A123T', count: 100 }] }, + }, + { + body: { minProportion: 0 }, + response: { data: [{ mutation: 'A123T', count: 200 }] }, + }, + ]); + lapisRouteMocker.mockPostAggregated({ pangoLineage: 'XEC' }, { data: [{ count: 150 }] }); + + const result = await fetchWasapPageData( + config, + {}, + { + mode: WASAP_ANALYSIS_MODE.variant, + signatureType: SIGNATURE_TYPE.computed, + sequenceType: SEQUENCE_TYPE.nucleotide, + variant: 'XEC', + minProportion: 0.8, + minCount: 15, + minJaccard: 0.3, + timeFrame: VARIANT_TIME_FRAME.all, + }, + ); + + // Jaccard for A123T: 100 / (150 + 200 - 100) = 0.4, which passes minJaccard=0.3 + expect(result).toEqual({ + type: 'mutations', + displayMutations: ['A123T'], + // eslint-disable-next-line @typescript-eslint/naming-convention + customColumns: [{ header: 'Jaccard index', values: { A123T: (0.4).toPrecision(2) } }], + }); + }); + + test('predefined signature: fetches collection from backend and returns mutations with empty jaccard', async () => { + backendRouteMocker.mockGetCollection('1', { + id: 1, + name: 'XEC', + ownedBy: 1, + organism: 'sc2', + description: null, + variantCount: 1, + tags: [], + variants: [ + { + type: 'filterObject', + id: 1, + collectionId: 1, + name: 'Nucleotide substitutions', + description: null, + filterObject: { nucleotideMutations: ['A123T', 'G456C'] }, + }, + ], + } as unknown as Collection); + // Empty clinical LAPIS data → jaccard map is empty → mutations returned unfiltered + lapisRouteMocker.mockPostNucleotideMutationsMulti([ + { body: { pangoLineage: 'XEC*', minProportion: 0 }, response: { data: [] } }, + { body: { minProportion: 0 }, response: { data: [] } }, + ]); + lapisRouteMocker.mockPostAggregated({ pangoLineage: 'XEC*' }, { data: [{ count: 0 }] }); + + const result = await fetchWasapPageData( + config, + {}, + { + mode: WASAP_ANALYSIS_MODE.variant, + signatureType: SIGNATURE_TYPE.predefined, + sequenceType: SEQUENCE_TYPE.nucleotide, + minProportion: -1, + minCount: -1, + minJaccard: -1, + timeFrame: VARIANT_TIME_FRAME.all, + collectionId: 1, + includeSublineagesForJaccard: true, + }, + ); + + expect(result).toEqual({ + type: 'mutations', + displayMutations: ['A123T', 'G456C'], + lineageForJaccard: 'XEC*', + // TODO - are we sure this makes sense? we don't have the custom column here + // ... it would be good to have a test that has the custom column here, probably. + }); + }); + + test('throws when mode is not enabled', async () => { + const disabledConfig = { ...baseConfigFields }; + + await expect( + fetchWasapPageData( + disabledConfig, + {}, + { + mode: WASAP_ANALYSIS_MODE.variant, + signatureType: SIGNATURE_TYPE.computed, + sequenceType: SEQUENCE_TYPE.nucleotide, + variant: 'XEC', + minProportion: 0.8, + minCount: 15, + minJaccard: 0.3, + timeFrame: VARIANT_TIME_FRAME.all, + }, + ), + ).rejects.toThrow("Cannot fetch data, 'variant' mode is not enabled."); + }); + }); + + describe('untracked mode', () => { + const config = { + ...baseConfigFields, + lapisBaseUrl: DUMMY_LAPIS_URL, + untrackedAnalysisModeEnabled: true as const, + clinicalLapis: { + lapisBaseUrl: DUMMY_LAPIS_URL, + cladeField: 'clade', + lineageField: 'pangoLineage', + }, + filterDefaults: { + untracked: { mode: WASAP_ANALYSIS_MODE.untracked, sequenceType: SEQUENCE_TYPE.nucleotide }, + }, + }; + + test('custom exclude set: filters out mutations belonging to the specified variants', async () => { + lapisRouteMocker.mockPostNucleotideMutationsMulti([ + { + body: { pangoLineage: 'XEC', minProportion: 0.8 }, + response: { data: [{ mutation: 'A123T', count: 100 }] }, + }, + { + body: { pangoLineage: 'JN.1', minProportion: 0.8 }, + response: { data: [{ mutation: 'G456C', count: 50 }] }, + }, + { + body: { minProportion: 0.05 }, + response: { + data: [ + { mutation: 'A123T', count: 200 }, + { mutation: 'G456C', count: 150 }, + { mutation: 'C789T', count: 10 }, + ], + }, + }, + ]); + + const result = await fetchWasapPageData( + config, + {}, + { + mode: WASAP_ANALYSIS_MODE.untracked, + sequenceType: SEQUENCE_TYPE.nucleotide, + excludeSet: EXCLUDE_SET_NAME.custom, + excludeVariants: ['XEC', 'JN.1'], + }, + ); + + expect(result).toEqual({ type: 'mutations', displayMutations: ['C789T'] }); + }); + + test('predefined exclude set: derives excluded variants from clade-lineage mapping', async () => { + lapisRouteMocker.mockPostAggregated( + { + fields: ['clade', 'pangoLineage'], + orderBy: ['clade', { field: 'count', type: 'descending' }], + }, + { data: [{ clade: '24A', pangoLineage: 'JN.1', count: 1000 }] }, + ); + lapisRouteMocker.mockPostNucleotideMutationsMulti([ + { + body: { pangoLineage: 'JN.1*', minProportion: 0.8 }, + response: { data: [{ mutation: 'A123T', count: 100 }] }, + }, + { + body: { minProportion: 0.05 }, + response: { + data: [ + { mutation: 'A123T', count: 200 }, + { mutation: 'C789T', count: 10 }, + ], + }, + }, + ]); + + const result = await fetchWasapPageData( + config, + {}, + { + mode: WASAP_ANALYSIS_MODE.untracked, + sequenceType: SEQUENCE_TYPE.nucleotide, + excludeSet: EXCLUDE_SET_NAME.predefined, + }, + ); + + expect(result).toEqual({ type: 'mutations', displayMutations: ['C789T'] }); + }); + + test('throws when mode is not enabled', async () => { + const disabledConfig = { ...baseConfigFields }; + + await expect( + fetchWasapPageData( + disabledConfig, + {}, + { mode: WASAP_ANALYSIS_MODE.untracked, sequenceType: SEQUENCE_TYPE.nucleotide }, + ), + ).rejects.toThrow("Cannot fetch data, 'untracked' mode is not enabled."); + }); + }); + + describe('covSpectrumCollection mode', () => { + const config = { + ...baseConfigFields, + lapisBaseUrl: DUMMY_LAPIS_URL, + covSpectrumCollectionAnalysisModeEnabled: true as const, + collectionsApiBaseUrl: DUMMY_COV_SPECTRUM_URL, + collectionTitleFilter: '', + filterDefaults: { + covSpectrumCollection: { mode: WASAP_ANALYSIS_MODE.covSpectrumCollection, collectionId: 42 }, + }, + }; + + test('fetches collection from CovSpectrum and builds queries for each variant', async () => { + testServer.use( + http.get(`${DUMMY_COV_SPECTRUM_URL}/resource/collection/42`, () => + Response.json({ + id: 42, + title: 'Test Collection', + description: 'A test', + maintainers: 'Testers', + email: 'test@example.com', + variants: [ + { + query: JSON.stringify({ type: 'variantQuery', variantQuery: 'JN.1*' }), + name: 'JN.1', + description: '', + highlighted: false, + }, + { + query: JSON.stringify({ type: 'variantQuery', variantQuery: 'XEC*' }), + name: 'XEC', + description: 'XEC lineage', + highlighted: false, + }, + ], + }), + ), + ); + lapisRouteMocker.mockPostQueryParse( + { queries: ['JN.1*', 'XEC*'] }, + { + data: [ + { + type: 'success', + filter: { type: 'HasNucleotideMutation', sequenceName: 'main', position: 123 }, + }, + { + type: 'success', + filter: { type: 'HasNucleotideMutation', sequenceName: 'main', position: 456 }, + }, + ], + }, + ); + + const result = await fetchWasapPageData( + config, + {}, + { mode: WASAP_ANALYSIS_MODE.covSpectrumCollection, collectionId: 42 }, + ); + + expect(result).toEqual({ + type: 'collection', + collection: { + id: 42, + title: 'Test Collection', + queries: [ + { + displayLabel: 'JN.1', + description: undefined, + countQuery: 'JN.1*', + coverageQuery: '(JN.1*) or (not maybe(JN.1*))', + }, + { + displayLabel: 'XEC', + description: 'XEC lineage', + countQuery: 'XEC*', + coverageQuery: '(XEC*) or (not maybe(XEC*))', + }, + ], + }, + }); + }); + + test('reports variants that fail query parsing as invalid', async () => { + testServer.use( + http.get(`${DUMMY_COV_SPECTRUM_URL}/resource/collection/42`, () => + Response.json({ + id: 42, + title: 'Test Collection', + description: '', + maintainers: 'test', + email: 'test@example.com', + variants: [ + { + query: JSON.stringify({ type: 'variantQuery', variantQuery: 'XEC*' }), + name: 'XEC', + description: '', + highlighted: false, + }, + { + query: JSON.stringify({ type: 'variantQuery', variantQuery: 'bad query!' }), + name: 'Bad', + description: '', + highlighted: false, + }, + ], + }), + ), + ); + lapisRouteMocker.mockPostQueryParse( + { queries: ['XEC*', 'bad query!'] }, + { + data: [ + { + type: 'success', + filter: { type: 'HasNucleotideMutation', sequenceName: 'main', position: 123 }, + }, + { type: 'failure', error: 'Unexpected token' }, + ], + }, + ); + + const result = await fetchWasapPageData( + config, + {}, + { mode: WASAP_ANALYSIS_MODE.covSpectrumCollection, collectionId: 42 }, + ); + + expect(result).toMatchObject({ + type: 'collection', + invalidVariants: [{ name: 'Bad', error: expect.stringContaining('Parse error') }], + }); + }); + + test('throws when mode is not enabled', async () => { + const disabledConfig = { ...baseConfigFields }; + + await expect( + fetchWasapPageData( + disabledConfig, + {}, + { mode: WASAP_ANALYSIS_MODE.covSpectrumCollection, collectionId: 42 }, + ), + ).rejects.toThrow("Cannot fetch data, 'covSpectrumCollection' mode is not enabled."); + }); + + test('throws when no collection is selected', async () => { + await expect( + fetchWasapPageData( + config, + {}, + { mode: WASAP_ANALYSIS_MODE.covSpectrumCollection, collectionId: undefined }, + ), + ).rejects.toThrow('No collection selected'); + }); + }); +}); + +describe('getLapisFilterForTimeFrame', () => { + test('"all" returns an empty filter', () => { + expect(getLapisFilterForTimeFrame('all', 'date')).toEqual({}); + }); + + test('"6months" returns a dateFrom filter using the given field name', () => { + const result = getLapisFilterForTimeFrame('6months', 'collectionDate'); + + expect(result).toEqual({ collectionDateFrom: dayjs().subtract(6, 'month').format('YYYY-MM-DD') }); + }); + + test('"3months" returns a dateFrom filter using the given field name', () => { + const result = getLapisFilterForTimeFrame('3months', 'date'); + + expect(result).toEqual({ dateFrom: dayjs().subtract(3, 'month').format('YYYY-MM-DD') }); + }); +}); diff --git a/website/src/components/views/wasap/useWasapPageData.ts b/website/src/components/views/wasap/useWasapPageData.ts index 23471b873..6f5e9ba4e 100644 --- a/website/src/components/views/wasap/useWasapPageData.ts +++ b/website/src/components/views/wasap/useWasapPageData.ts @@ -37,7 +37,7 @@ export function useWasapPageData( }); } -async function fetchWasapPageData( +export async function fetchWasapPageData( config: WasapPageConfig, resistanceMutationsBySet: Record, analysis: WasapAnalysisFilter, diff --git a/website/src/components/views/wasap/wasapPageConfig.ts b/website/src/components/views/wasap/wasapPageConfig.ts index 7c1d5639d..53e175fd9 100644 --- a/website/src/components/views/wasap/wasapPageConfig.ts +++ b/website/src/components/views/wasap/wasapPageConfig.ts @@ -1,5 +1,10 @@ import type { DateRangeOption, SequenceType, TemporalGranularity } from '@genspectrum/dashboard-components/util'; +export const SEQUENCE_TYPE = { + nucleotide: 'nucleotide', + aminoAcid: 'amino acid', +} as const satisfies Record; + /** * All config settings for a W-ASAP dashboard page. */ @@ -164,7 +169,15 @@ export type LinkTemplate = { aminoAcidMutation: string; }; -export type WasapAnalysisMode = 'manual' | 'variant' | 'resistance' | 'untracked' | 'covSpectrumCollection'; +export const WASAP_ANALYSIS_MODE = { + manual: 'manual', + variant: 'variant', + resistance: 'resistance', + untracked: 'untracked', + covSpectrumCollection: 'covSpectrumCollection', +} as const; + +export type WasapAnalysisMode = (typeof WASAP_ANALYSIS_MODE)[keyof typeof WASAP_ANALYSIS_MODE]; /** * Contains mode-independent settings, like the filter for location and date range. @@ -205,11 +218,16 @@ export function variantTimeFrameLabel(timeFrame: VariantTimeFrame): string { } } +export const SIGNATURE_TYPE = { + computed: 'computed', + predefined: 'predefined', +} as const; + /** * The type of variant mutation signature. `predefined` is a pre-defined list pulled from online, * `computed` computes the list of signature mutations for a variant based on user parameters. */ -export type SignatureType = 'computed' | 'predefined'; +export type SignatureType = (typeof SIGNATURE_TYPE)[keyof typeof SIGNATURE_TYPE]; export type WasapVariantFilter = { mode: 'variant'; @@ -233,7 +251,12 @@ export type WasapResistanceFilter = { resistanceSet: string; }; -export type ExcludeSetName = 'predefined' | 'custom'; +export const EXCLUDE_SET_NAME = { + predefined: 'predefined', + custom: 'custom', +} as const; + +export type ExcludeSetName = (typeof EXCLUDE_SET_NAME)[keyof typeof EXCLUDE_SET_NAME]; export type WasapUntrackedFilter = { mode: 'untracked'; From fd9255e2452bee864f58333a0ac32865656e33e5 Mon Sep 17 00:00:00 2001 From: Felix Hennig Date: Mon, 13 Jul 2026 11:01:24 +0200 Subject: [PATCH 2/3] test(wasap): add predefined signature test with jaccard filtering and customColumns Co-Authored-By: Claude Sonnet 4.6 --- .../views/wasap/useWasapPageData.spec.ts | 71 ++++++++++++++++++- 1 file changed, 69 insertions(+), 2 deletions(-) diff --git a/website/src/components/views/wasap/useWasapPageData.spec.ts b/website/src/components/views/wasap/useWasapPageData.spec.ts index fbb51f803..fd57825a7 100644 --- a/website/src/components/views/wasap/useWasapPageData.spec.ts +++ b/website/src/components/views/wasap/useWasapPageData.spec.ts @@ -220,8 +220,75 @@ describe('fetchWasapPageData', () => { type: 'mutations', displayMutations: ['A123T', 'G456C'], lineageForJaccard: 'XEC*', - // TODO - are we sure this makes sense? we don't have the custom column here - // ... it would be good to have a test that has the custom column here, probably. + }); + }); + + test('predefined signature: filters mutations by jaccard and returns customColumns', async () => { + backendRouteMocker.mockGetCollection('1', { + id: 1, + name: 'XEC', + ownedBy: 1, + organism: 'sc2', + description: null, + variantCount: 1, + tags: [], + variants: [ + { + type: 'filterObject', + id: 1, + collectionId: 1, + name: 'Nucleotide substitutions', + description: null, + filterObject: { nucleotideMutations: ['A123T', 'G456C'] }, + }, + ], + } as unknown as Collection); + lapisRouteMocker.mockPostNucleotideMutationsMulti([ + { + body: { pangoLineage: 'XEC*', minProportion: 0 }, + response: { + data: [ + { mutation: 'A123T', count: 100 }, + { mutation: 'G456C', count: 10 }, + ], + }, + }, + { + body: { minProportion: 0 }, + response: { + data: [ + { mutation: 'A123T', count: 200 }, + { mutation: 'G456C', count: 200 }, + ], + }, + }, + ]); + lapisRouteMocker.mockPostAggregated({ pangoLineage: 'XEC*' }, { data: [{ count: 150 }] }); + + const result = await fetchWasapPageData( + config, + {}, + { + mode: WASAP_ANALYSIS_MODE.variant, + signatureType: SIGNATURE_TYPE.predefined, + sequenceType: SEQUENCE_TYPE.nucleotide, + minProportion: -1, + minCount: -1, + minJaccard: 0.3, + timeFrame: VARIANT_TIME_FRAME.all, + collectionId: 1, + includeSublineagesForJaccard: true, + }, + ); + + // Jaccard for A123T: 100 / (150 + 200 - 100) = 0.4, passes minJaccard=0.3 + // Jaccard for G456C: 10 / (150 + 200 - 10) ≈ 0.029, fails minJaccard=0.3 + expect(result).toEqual({ + type: 'mutations', + displayMutations: ['A123T'], + lineageForJaccard: 'XEC*', + // eslint-disable-next-line @typescript-eslint/naming-convention + customColumns: [{ header: 'Jaccard index', values: { A123T: (0.4).toPrecision(2) } }], }); }); From 4a8b7b8ede15969eff3146de3b514c5b648c2e22 Mon Sep 17 00:00:00 2001 From: Felix Hennig Date: Mon, 13 Jul 2026 11:07:25 +0200 Subject: [PATCH 3/3] test(wasap): use VARIANT_TIME_FRAME constants in getLapisFilterForTimeFrame tests Co-Authored-By: Claude Sonnet 4.6 --- website/src/components/views/wasap/useWasapPageData.spec.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/website/src/components/views/wasap/useWasapPageData.spec.ts b/website/src/components/views/wasap/useWasapPageData.spec.ts index fd57825a7..4a2d97a98 100644 --- a/website/src/components/views/wasap/useWasapPageData.spec.ts +++ b/website/src/components/views/wasap/useWasapPageData.spec.ts @@ -575,17 +575,17 @@ describe('fetchWasapPageData', () => { describe('getLapisFilterForTimeFrame', () => { test('"all" returns an empty filter', () => { - expect(getLapisFilterForTimeFrame('all', 'date')).toEqual({}); + expect(getLapisFilterForTimeFrame(VARIANT_TIME_FRAME.all, 'date')).toEqual({}); }); test('"6months" returns a dateFrom filter using the given field name', () => { - const result = getLapisFilterForTimeFrame('6months', 'collectionDate'); + const result = getLapisFilterForTimeFrame(VARIANT_TIME_FRAME.sixMonths, 'collectionDate'); expect(result).toEqual({ collectionDateFrom: dayjs().subtract(6, 'month').format('YYYY-MM-DD') }); }); test('"3months" returns a dateFrom filter using the given field name', () => { - const result = getLapisFilterForTimeFrame('3months', 'date'); + const result = getLapisFilterForTimeFrame(VARIANT_TIME_FRAME.threeMonths, 'date'); expect(result).toEqual({ dateFrom: dayjs().subtract(3, 'month').format('YYYY-MM-DD') }); });