diff --git a/frontend/__tests__/unit/components/ChapterMap.test.tsx b/frontend/__tests__/unit/components/ChapterMap.test.tsx index d16027d496..bbdc01e5f5 100644 --- a/frontend/__tests__/unit/components/ChapterMap.test.tsx +++ b/frontend/__tests__/unit/components/ChapterMap.test.tsx @@ -1,4 +1,5 @@ import { render } from '@testing-library/react' +import * as L from 'leaflet' import { Chapter } from 'types/chapter' import ChapterMap from 'components/ChapterMap' @@ -113,8 +114,6 @@ describe('ChapterMap', () => { describe('Map initialization', () => { it('initializes leaflet map with correct configuration', () => { - // eslint-disable-next-line @typescript-eslint/no-require-imports - const L = require('leaflet') render() expect(L.map).toHaveBeenCalledWith('chapter-map', { worldCopyJump: false, @@ -128,9 +127,6 @@ describe('ChapterMap', () => { }) it('adds tile layer to the map', () => { - // eslint-disable-next-line @typescript-eslint/no-require-imports - const L = require('leaflet') - render() expect(L.tileLayer).toHaveBeenCalledWith( 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', @@ -143,9 +139,6 @@ describe('ChapterMap', () => { }) it('creates and adds marker cluster group', () => { - // eslint-disable-next-line @typescript-eslint/no-require-imports - const L = require('leaflet') - render() expect(L.markerClusterGroup).toHaveBeenCalled() expect(mockMap.addLayer).toHaveBeenCalledWith(mockMarkerClusterGroup) @@ -154,19 +147,45 @@ describe('ChapterMap', () => { describe('Markers', () => { it('creates markers for each chapter', () => { - // eslint-disable-next-line @typescript-eslint/no-require-imports - const L = require('leaflet') - render() expect(L.marker).toHaveBeenCalledTimes(2) expect(L.marker).toHaveBeenCalledWith([40.7128, -74.006], { icon: mockIcon }) expect(L.marker).toHaveBeenCalledWith([51.5074, -0.1278], { icon: mockIcon }) }) - it('creates marker icons with correct configuration', () => { - // eslint-disable-next-line @typescript-eslint/no-require-imports - const L = require('leaflet') + it('filters out virtual chapters when latitude longitude undefined', () => { + const virtualChapterData: Chapter[] = [ + mockChapterData[0], + { + // A virtual chapter with no location data + ...mockChapterData[1], + _geoloc: undefined, + geoLocation: undefined, + }, + ] + + render() + expect(L.marker).toHaveBeenCalledTimes(1) + expect(L.marker).not.toHaveBeenCalledWith([undefined, undefined], { icon: mockIcon }) + }) + + it('filters out virtual chapters when latitude longitude null', () => { + const virtualChapterData: Chapter[] = [ + mockChapterData[0], + { + // A virtual chapter with no location data + ...mockChapterData[1], + _geoloc: null, + geoLocation: null, + }, + ] + + render() + expect(L.marker).toHaveBeenCalledTimes(1) + expect(L.marker).not.toHaveBeenCalledWith([null, null], { icon: mockIcon }) + }) + it('creates marker icons with correct configuration', () => { render() expect(L.Icon).toHaveBeenCalledWith({ iconAnchor: [12, 41], @@ -185,8 +204,6 @@ describe('ChapterMap', () => { }) it('handles chapters with missing _geoloc but present geolocation', () => { - // eslint-disable-next-line @typescript-eslint/no-require-imports - const L = require('leaflet') const chapterWithoutGeoloc: Chapter[] = [ { ...mockChapterData[0], @@ -198,13 +215,24 @@ describe('ChapterMap', () => { render() expect(L.marker).toHaveBeenCalledWith([35.6762, 139.6503], { icon: mockIcon }) }) + + it('handles chapters with 0 coordinates correctly', () => { + const chapterWithZeroCoords: Chapter[] = [ + { + ...mockChapterData[0], + _geoloc: { lat: 0, lng: 0 }, + geoLocation: { lat: 0, lng: 0 }, + }, + ] + + render() + expect(L.marker).toHaveBeenCalledTimes(1) + expect(L.marker).toHaveBeenCalledWith([0, 0], { icon: mockIcon }) + }) }) describe('Popups', () => { it('creates popups for each marker', () => { - // eslint-disable-next-line @typescript-eslint/no-require-imports - const L = require('leaflet') - render() expect(L.popup).toHaveBeenCalledTimes(2) expect(mockMarker.bindPopup).toHaveBeenCalledTimes(2) @@ -223,8 +251,6 @@ describe('ChapterMap', () => { describe('Local View', () => { it('sets local view when showLocal is true', () => { - // eslint-disable-next-line @typescript-eslint/no-require-imports - const L = require('leaflet') render() expect(mockMap.setView).toHaveBeenCalledWith([40.7128, -74.006], 7) diff --git a/frontend/src/components/ChapterMap.tsx b/frontend/src/components/ChapterMap.tsx index 36c360aca5..e753adadea 100644 --- a/frontend/src/components/ChapterMap.tsx +++ b/frontend/src/components/ChapterMap.tsx @@ -48,7 +48,13 @@ const ChapterMap = ({ const markerClusterGroup = markerClusterRef.current - const markers = geoLocData.map((chapter) => { + const validGeoLocData = geoLocData.filter((chapter) => { + const lat = chapter._geoloc?.lat ?? chapter.geoLocation?.lat + const lng = chapter._geoloc?.lng ?? chapter.geoLocation?.lng + return typeof lat === 'number' && typeof lng === 'number' + }) + + const markers = validGeoLocData.map((chapter) => { const markerIcon = new L.Icon({ iconAnchor: [12, 41], iconRetinaUrl: '/img/marker-icon-2x.png', @@ -61,8 +67,8 @@ const ChapterMap = ({ const marker = L.marker( [ - chapter._geoloc?.lat || chapter.geoLocation?.lat, - chapter._geoloc?.lng || chapter.geoLocation?.lng, + chapter._geoloc?.lat ?? chapter.geoLocation?.lat, + chapter._geoloc?.lng ?? chapter.geoLocation?.lng, ], { icon: markerIcon } ) @@ -80,21 +86,21 @@ const ChapterMap = ({ markerClusterGroup.addLayers(markers) - if (showLocal && geoLocData.length > 0) { + if (showLocal && validGeoLocData.length > 0) { const maxNearestChapters = 5 - const localChapters = geoLocData.slice(0, maxNearestChapters - 1) + const localChapters = validGeoLocData.slice(0, maxNearestChapters - 1) const localBounds = L.latLngBounds( localChapters.map((chapter) => [ - chapter._geoloc?.lat || chapter.geoLocation?.lat, - chapter._geoloc?.lng || chapter.geoLocation?.lng, + chapter._geoloc?.lat ?? chapter.geoLocation?.lat, + chapter._geoloc?.lng ?? chapter.geoLocation?.lng, ]) ) const maxZoom = 7 - const nearestChapter = geoLocData[0] + const nearestChapter = validGeoLocData[0] map.setView( [ - nearestChapter._geoloc?.lat || nearestChapter.geoLocation?.lat, - nearestChapter._geoloc?.lng || nearestChapter.geoLocation?.lng, + nearestChapter._geoloc?.lat ?? nearestChapter.geoLocation?.lat, + nearestChapter._geoloc?.lng ?? nearestChapter.geoLocation?.lng, ], maxZoom )