import { describe, it, expect, vi, beforeEach } from 'vitest' import { mount, flushPromises } from '@vue/test-utils' import { ref, computed, watch, nextTick, onMounted, onBeforeUnmount } from 'vue' import AddressGeoPin from '../AddressGeoPin.vue' // Mock Leaflet (hoisted) : capture le handler `dragend` et pilote la position // renvoyee par getLatLng — permet de simuler un drag du marqueur sans DOM reel. const leafletState = vi.hoisted(() => ({ dragendHandler: null as (() => void) | null, markerPosition: { lat: 0, lng: 0 }, })) vi.mock('leaflet', () => { const marker = { addTo: vi.fn().mockReturnThis(), on: vi.fn((event: string, handler: () => void) => { if (event === 'dragend') { leafletState.dragendHandler = handler } }), getLatLng: vi.fn(() => leafletState.markerPosition), setLatLng: vi.fn(), } const map = { setView: vi.fn().mockReturnThis(), panTo: vi.fn(), remove: vi.fn(), } const L = { map: vi.fn(() => map), tileLayer: vi.fn(() => ({ addTo: vi.fn() })), divIcon: vi.fn(() => ({})), marker: vi.fn(() => marker), } return { default: L, ...L } }) vi.mock('leaflet/dist/leaflet.css', () => ({ default: {} })) // Mock controlable du geocodage BAN (bouton « Re-geocoder »). const { geocodeMock } = vi.hoisted(() => ({ geocodeMock: vi.fn() })) vi.mock('~/shared/composables/useAddressAutocomplete', () => ({ useAddressAutocomplete: () => ({ geocode: geocodeMock }), })) // Auto-imports Nuxt/Vue utilises sans import explicite par le composant. vi.stubGlobal('useI18n', () => ({ t: (key: string) => key })) vi.stubGlobal('ref', ref) vi.stubGlobal('computed', computed) vi.stubGlobal('watch', watch) vi.stubGlobal('nextTick', nextTick) vi.stubGlobal('onMounted', onMounted) vi.stubGlobal('onBeforeUnmount', onBeforeUnmount) interface PinProps { latitude?: string | null longitude?: string | null geoManual?: boolean geocodeQuery?: string | null readonly?: boolean } function mountPin(props: PinProps = {}) { return mount(AddressGeoPin, { props: { latitude: null, longitude: null, geoManual: false, geocodeQuery: '1 rue du Test, 86100 Châtellerault', ...props, }, global: { stubs: { MalioButton: true }, }, }) } beforeEach(() => { leafletState.dragendHandler = null geocodeMock.mockReset() }) describe('AddressGeoPin — adresse sans coordonnees', () => { it('affiche le badge « a geolocaliser » et aucune carte', () => { const wrapper = mountPin() expect(wrapper.find('[data-testid="geo-badge-missing"]').exists()).toBe(true) expect(wrapper.find('[data-testid="geo-map"]').exists()).toBe(false) }) }) describe('AddressGeoPin — drag du marqueur (RG-6.08)', () => { it('emet les coordonnees corrigees avec geoManual=true au dragend', async () => { const wrapper = mountPin({ latitude: '46.5802596', longitude: '0.3404333' }) await flushPromises() // import dynamique de Leaflet + montage carte expect(leafletState.dragendHandler).not.toBeNull() // L'utilisateur depose le pin ailleurs (lieu-dit mal geocode). leafletState.markerPosition = { lat: 48.1234567, lng: -1.6543217 } leafletState.dragendHandler?.() const emitted = wrapper.emitted('update:coords') expect(emitted).toHaveLength(1) expect(emitted?.[0]?.[0]).toEqual({ latitude: '48.1234567', longitude: '-1.6543217', geoManual: true, }) }) it('affiche le badge « pin manuel » quand geoManual est vrai', () => { const wrapper = mountPin({ latitude: '46.58', longitude: '0.34', geoManual: true }) expect(wrapper.find('[data-testid="geo-badge-manual"]').exists()).toBe(true) expect(wrapper.find('[data-testid="geo-badge-missing"]').exists()).toBe(false) }) }) describe('AddressGeoPin — re-geocodage depuis l\'adresse', () => { it('emet la position BAN avec geoManual=false (le back refera autorite au save)', async () => { geocodeMock.mockResolvedValueOnce({ latitude: '46.5802596', longitude: '0.3404333' }) const wrapper = mountPin() await wrapper.find('[data-testid="geo-regeocode"]').trigger('click') await flushPromises() expect(geocodeMock).toHaveBeenCalledWith('1 rue du Test, 86100 Châtellerault') expect(wrapper.emitted('update:coords')?.[0]?.[0]).toEqual({ latitude: '46.5802596', longitude: '0.3404333', geoManual: false, }) }) it('signale l\'echec sans emettre quand la BAN ne trouve rien', async () => { geocodeMock.mockResolvedValueOnce(null) const wrapper = mountPin() await wrapper.find('[data-testid="geo-regeocode"]').trigger('click') await flushPromises() expect(wrapper.emitted('update:coords')).toBeUndefined() expect(wrapper.find('[data-testid="geo-regeocode-failed"]').exists()).toBe(true) }) it('masque le bouton en lecture seule', () => { const wrapper = mountPin({ readonly: true }) expect(wrapper.find('[data-testid="geo-regeocode"]').exists()).toBe(false) }) })