diff --git a/.env b/.env index 150dfdb4..b5420fe2 100644 --- a/.env +++ b/.env @@ -3,6 +3,4 @@ VITE_VALHALLA_URL=https://valhalla1.openstreetmap.de VITE_NOMINATIM_URL=https://nominatim.openstreetmap.org VITE_TILE_SERVER_URL="https://tile.openstreetmap.org/{z}/{x}/{y}.png" VITE_CENTER_COORDS="52.51831,13.393707" - -# Possible values: auto, bicycle, pedestrian, car, truck, bus, motor_scooter, motorcycle -VITE_DEFAULT_COSTING_MODEL=bicycle +VITE_DEFAULT_COSTING_MODEL=bicycle \ No newline at end of file diff --git a/src/components/settings-panel/settings-panel.spec.tsx b/src/components/settings-panel/settings-panel.spec.tsx index 2a6c1c88..5ab8ae45 100644 --- a/src/components/settings-panel/settings-panel.spec.tsx +++ b/src/components/settings-panel/settings-panel.spec.tsx @@ -158,10 +158,10 @@ describe('SettingsPanel', () => { ).toBeInTheDocument(); }); - it('should render Reset button', () => { + it('should render Reset button', async () => { renderWithQueryClient(); expect( - screen.getByRole('button', { name: /^Reset$/i }) + await screen.findByRole('button', { name: /^Reset$/i }) ).toBeInTheDocument(); }); diff --git a/src/utils/nominatim.spec.ts b/src/utils/nominatim.spec.ts new file mode 100644 index 00000000..ad1c975f --- /dev/null +++ b/src/utils/nominatim.spec.ts @@ -0,0 +1,66 @@ +import { describe, it, expect, beforeEach } from 'vitest'; +import { parseGeocodeResponse } from './nominatim'; +import type { NominationResponse } from '@/components/types'; + +describe('parseGeocodeResponse', () => { + let counter = 0; + + beforeEach(() => { + counter = 0; + }); + + const makeResult = (name: string, lat?: string, lon?: string) => + ({ + display_name: name, + lat: lat ?? `${++counter}.0000`, + lon: lon ?? `${++counter}.0000`, + osm_type: 'node', + osm_id: counter, + }) as unknown as NominationResponse; + + it('returns all results when there are no duplicates', () => { + const results = [makeResult('Place A'), makeResult('Place B')]; + + const processed = parseGeocodeResponse(results); + expect(processed).toHaveLength(2); + expect(processed[0]!.title).toBe('Place A'); + expect(processed[1]!.title).toBe('Place B'); + }); + + it('removes entries that have identical coordinates and same name after diacritic normalization', () => { + const lat = `${++counter}.0000`; + const lon = `${++counter}.0000`; + + const results = [ + makeResult('Pláce C', lat, lon), + makeResult('Place C', lat, lon), + makeResult('PLÁCE C', lat, lon), + ]; + + const processed = parseGeocodeResponse(results); + expect(processed).toHaveLength(1); + expect(processed[0]!.title).toBe('Pláce C'); + }); + + it('keeps entries that have the same name but genuinely different coordinates', () => { + const results = [ + makeResult('Place D'), + makeResult('Place D'), + makeResult('Place D'), + ]; + + const processed = parseGeocodeResponse(results); + expect(processed).toHaveLength(3); + expect(processed[0]!.title).toBe('Place D'); + expect(processed[1]!.title).toBe('Place D'); + expect(processed[2]!.title).toBe('Place D'); + }); + + it('handles a single non-array result without throwing', () => { + const result = makeResult('Place E'); + + const processed = parseGeocodeResponse(result); + expect(processed).toHaveLength(1); + expect(processed[0]!.title).toBe('Place E'); + }); +}); diff --git a/src/utils/nominatim.ts b/src/utils/nominatim.ts index 686c3ff5..30f4b5fa 100644 --- a/src/utils/nominatim.ts +++ b/src/utils/nominatim.ts @@ -31,6 +31,8 @@ export const parseGeocodeResponse = ( } const processedResults = []; + const seenKeys = new Set(); + for (const [index, result] of results.entries()) { if ( 'error' in result && @@ -48,6 +50,19 @@ export const parseGeocodeResponse = ( addressindex: index, }); } else { + const normalizedTitle = result.display_name + .toLowerCase() + .normalize('NFD') + .replace(/[\u0300-\u036f]/g, ''); + const dedupeKey = result.boundingbox + ? `${normalizedTitle}${result.boundingbox.join(',')}` + : `${normalizedTitle}${result.lat}${result.lon}`; + + if (seenKeys.has(dedupeKey)) { + continue; + } + seenKeys.add(dedupeKey); + processedResults.push({ title: result.display_name.length > 0