import React from 'react' import { describe, it, expect, vi, afterEach, beforeEach } from 'vitest' import { render } from '../../../tests/helpers/render' import { act } from '@testing-library/react' import { resetAllStores } from '../../../tests/helpers/store' import { buildPlace } from '../../../tests/helpers/factories' import { useSettingsStore } from '../../store/settingsStore' // Stable fake map so fitBounds call counts survive re-renders. const glMap = vi.hoisted(() => ({ on: vi.fn(), off: vi.fn(), once: vi.fn(), loaded: vi.fn().mockReturnValue(true), fitBounds: vi.fn(), flyTo: vi.fn(), jumpTo: vi.fn(), getZoom: vi.fn().mockReturnValue(10), addControl: vi.fn(), removeControl: vi.fn(), remove: vi.fn(), addSource: vi.fn(), getSource: vi.fn().mockReturnValue(null), addLayer: vi.fn(), setLayoutProperty: vi.fn(), getStyle: vi.fn().mockReturnValue({ layers: [] }), isStyleLoaded: vi.fn().mockReturnValue(true), getCanvasContainer: vi.fn(() => document.createElement('div')), })) vi.mock('mapbox-gl', () => ({ default: { accessToken: '', Map: vi.fn(function () { return glMap }), Marker: vi.fn(function () { return { setLngLat: vi.fn().mockReturnThis(), addTo: vi.fn().mockReturnThis(), remove: vi.fn(), getElement: vi.fn(() => document.createElement('div')), } }), LngLatBounds: vi.fn(function () { return { extend: vi.fn().mockReturnThis() } }), NavigationControl: vi.fn(), Popup: vi.fn(function () { return { setLngLat: vi.fn().mockReturnThis(), setHTML: vi.fn().mockReturnThis(), addTo: vi.fn().mockReturnThis(), remove: vi.fn(), } }), }, })) vi.mock('mapbox-gl/dist/mapbox-gl.css', () => ({})) vi.mock('./mapboxSetup', () => ({ isStandardFamily: vi.fn(() => false), supportsCustom3d: vi.fn(() => false), wantsTerrain: vi.fn(() => false), addCustom3dBuildings: vi.fn(), addTerrainAndSky: vi.fn(), })) vi.mock('./locationMarkerMapbox', () => ({ attachLocationMarker: vi.fn(() => ({ update: vi.fn() })), })) vi.mock('./reservationsMapbox', () => ({ ReservationMapboxOverlay: vi.fn(function () { return { update: vi.fn() } }), })) vi.mock('../../hooks/useGeolocation', () => ({ useGeolocation: vi.fn(() => ({ position: null, mode: 'off', error: null, cycleMode: vi.fn(), setMode: vi.fn(), })), })) vi.mock('../../services/photoService', () => ({ getCached: vi.fn(() => null), isLoading: vi.fn(() => false), fetchPhoto: vi.fn(), onThumbReady: vi.fn(() => () => {}), getAllThumbs: vi.fn(() => ({})), })) import { MapViewGL } from './MapViewGL' function buildMapPlace(overrides: Record = {}) { return { ...buildPlace(), category_name: null, category_color: null, category_icon: null, ...overrides, } as any } beforeEach(() => { useSettingsStore.setState({ settings: { ...useSettingsStore.getState().settings, map_provider: 'mapbox-gl', mapbox_access_token: 'pk.test_token', mapbox_style: 'mapbox://styles/mapbox/streets-v12', mapbox_3d_enabled: false, }, } as any) }) afterEach(() => { vi.clearAllMocks() resetAllStores() }) describe('MapViewGL', () => { it('FE-COMP-MAPVIEWGL-001: opening place inspector does not refit bounds (issue #921)', async () => { const places = [ buildMapPlace({ id: 1, lat: 48.8584, lng: 2.2945 }), buildMapPlace({ id: 2, lat: 48.86, lng: 2.337 }), ] const { rerender } = render( , ) await act(async () => {}) const after_initial = glMap.fitBounds.mock.calls.length // Selecting a place flips hasInspector → paddingOpts memo changes. // fitBounds must NOT fire again (this was the bug). rerender( , ) await act(async () => {}) expect(glMap.fitBounds).toHaveBeenCalledTimes(after_initial) }) it('FE-COMP-MAPVIEWGL-002: closing inspector does not refit bounds (issue #921)', async () => { const places = [ buildMapPlace({ id: 1, lat: 48.8584, lng: 2.2945 }), ] const { rerender } = render( , ) await act(async () => {}) const after_initial = glMap.fitBounds.mock.calls.length // Closing inspector (X button) clears selectedPlaceId → hasInspector=false → new paddingOpts. rerender( , ) await act(async () => {}) expect(glMap.fitBounds).toHaveBeenCalledTimes(after_initial) }) it('FE-COMP-MAPVIEWGL-003: bumping fitKey triggers a new fitBounds call', async () => { const places = [ buildMapPlace({ id: 1, lat: 48.8584, lng: 2.2945 }), ] const { rerender } = render() await act(async () => {}) const after_first = glMap.fitBounds.mock.calls.length rerender() await act(async () => {}) expect(glMap.fitBounds.mock.calls.length).toBeGreaterThan(after_first) }) })