import { render, screen } from '@testing-library/react'; import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest'; import type { ValuationComparable } from '@/lib/valuation-api'; import { ComparablesMap } from '../comparables-map'; // Mapbox GL does not run cleanly in jsdom — mock with a minimal stand-in // that records addTo calls so we can assert marker count. const markerAddTo = vi.fn(); const mapAddControl = vi.fn(); const mapFitBounds = vi.fn(); const mapFlyTo = vi.fn(); const mapRemove = vi.fn(); vi.mock('mapbox-gl', () => { class MockMap { addControl = mapAddControl; fitBounds = mapFitBounds; flyTo = mapFlyTo; remove = mapRemove; setStyle = () => undefined; on = () => undefined; } class MockNavigationControl {} class MockAttributionControl {} class MockMarker { setLngLat() { return this; } setPopup() { return this; } addTo() { markerAddTo(); return this; } remove() { // noop } } class MockPopup { setHTML() { return this; } } class MockLngLatBounds { extend() { return this; } isEmpty() { return false; } } return { default: { accessToken: '', Map: MockMap, NavigationControl: MockNavigationControl, AttributionControl: MockAttributionControl, Marker: MockMarker, Popup: MockPopup, LngLatBounds: MockLngLatBounds, }, }; }); vi.mock('mapbox-gl/dist/mapbox-gl.css', () => ({})); const sampleComparables: ValuationComparable[] = [ { id: 'comp-1', title: 'Căn hộ tương tự A', address: '456 Nguyễn Hữu Thọ', district: 'Quận 7', priceVND: '4800000000', areaM2: 78, pricePerM2: 61_500_000, similarity: 0.92, latitude: 10.73, longitude: 106.72, }, { id: 'comp-2', title: 'Căn hộ tương tự B', address: '789 Phạm Viết Chánh', district: 'Bình Thạnh', priceVND: '5200000000', areaM2: 82, pricePerM2: 63_400_000, similarity: 0.7, latitude: 10.8, longitude: 106.7, }, ]; describe('ComparablesMap', () => { beforeEach(() => { markerAddTo.mockClear(); mapAddControl.mockClear(); mapFitBounds.mockClear(); mapFlyTo.mockClear(); mapRemove.mockClear(); process.env['NEXT_PUBLIC_MAPBOX_TOKEN'] = 'pk.test'; }); afterEach(() => { delete (process.env as Record)[ 'NEXT_PUBLIC_MAPBOX_TOKEN' ]; }); it('renders header and descriptor', () => { render(); expect(screen.getByText('Bản đồ so sánh')).toBeInTheDocument(); expect(screen.getByText(/2 BĐS so sánh/)).toBeInTheDocument(); }); it('renders prompt when mapbox token is missing', () => { delete (process.env as Record)[ 'NEXT_PUBLIC_MAPBOX_TOKEN' ]; render(); expect( screen.getByText(/Thiết lập NEXT_PUBLIC_MAPBOX_TOKEN/), ).toBeInTheDocument(); }); it('shows empty state when no comparables have coordinates', () => { const withoutCoords = sampleComparables.map( ({ latitude: _lat, longitude: _lng, ...rest }) => rest, ); render(); expect( screen.getByText(/Không có toạ độ cho các BĐS so sánh/), ).toBeInTheDocument(); }); it('adds a marker for each geolocated comparable plus subject pin', () => { render( , ); expect(markerAddTo).toHaveBeenCalledTimes(3); }); });