import { render, screen, fireEvent } from '@testing-library/react'; import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; import React from 'react'; vi.doMock('../google-maps-loader', () => ({ useGoogleMapsLoader: () => ({ isLoaded: true, loadError: undefined, load: async () => {} }), })); const POA = { lat: -30.03, lng: -51.22 }; describe('Map — field and forensics', () => { let Map: any; const polygonInstances: any[] = []; const polylineInstances: any[] = []; const mapInstance: any = { addListener: vi.fn(() => ({ remove: vi.fn() })), getZoom: vi.fn(() => 12), getCenter: vi.fn(() => ({ lat: () => -30, lng: () => -51 })), }; beforeEach(async () => { vi.clearAllMocks(); polygonInstances.length = 0; polylineInstances.length = 0; Map = (await import('./map')).Map; Object.defineProperty(HTMLElement.prototype, 'innerMap', { configurable: true, get() { return this.tagName === 'GMP-MAP' ? mapInstance : undefined; }, }); const shape = (bucket: any[]) => vi.fn(function (options: any) { const instance = { options, setMap: vi.fn(), setOptions: vi.fn(function (next: any) { instance.options = { ...instance.options, ...next }; }), addListener: vi.fn(() => ({ remove: vi.fn() })), }; bucket.push(instance); return instance; }); global.google = { maps: { Map: vi.fn(function () { return mapInstance; }), Polygon: shape(polygonInstances), Polyline: shape(polylineInstances), Circle: shape([]), InfoWindow: vi.fn(function () { return { open: vi.fn(), close: vi.fn(), setContent: vi.fn() }; }), LatLng: vi.fn(function (lat: number, lng: number) { return { lat, lng }; }), LatLngBounds: vi.fn(function () { return { extend: vi.fn() }; }), Data: vi.fn(function () { return { setMap: vi.fn(), addGeoJson: vi.fn(), setStyle: vi.fn(), forEach: vi.fn(), remove: vi.fn(), revertStyle: vi.fn(), overrideStyle: vi.fn(), addListener: vi.fn(() => ({ remove: vi.fn() })), }; }), drawing: { DrawingManager: vi.fn(function () { return { setMap: vi.fn(), setOptions: vi.fn(), setDrawingMode: vi.fn(), addListener: vi.fn(() => ({ remove: vi.fn() })), }; }), }, visualization: { HeatmapLayer: vi.fn(function () { return { setMap: vi.fn(), setOptions: vi.fn() }; }), }, event: { addListener: vi.fn(() => ({ remove: vi.fn() })), addListenerOnce: vi.fn(() => ({ remove: vi.fn() })), }, marker: { AdvancedMarkerElement: vi.fn() }, }, } as any; }); afterEach(() => { delete (HTMLElement.prototype as any).innerMap; delete (global as any).google; vi.resetModules(); }); /* --- C2 ------------------------------------------------------------------ */ it('draws an azimuth sector as a closed wedge', () => { render( ); expect(polygonInstances).toHaveLength(1); const path = polygonInstances[0].options.paths; // Vertex at the site on both ends, so it reads as a beam. expect(path[0]).toEqual(POA); expect(path[path.length - 1]).toEqual(POA); }); /* --- C3 ------------------------------------------------------------------ */ describe('polylines', () => { it('draws a line through arbitrary coordinates', () => { render( ); expect(polylineInstances).toHaveLength(1); expect(polylineInstances[0].options.strokeWeight).toBe(4); }); it('renders a dash as repeated symbols with an invisible stroke', () => { render( ); // Polyline has no dash property; the documented technique is icon repeat. expect(polylineInstances[0].options.strokeOpacity).toBe(0); expect(polylineInstances[0].options.icons[0].repeat).toBe('11px'); }); }); /* --- B6 ------------------------------------------------------------------ */ describe('annotations', () => { it('renders user-authored pins with their author in the accessible name', () => { render( ); expect(screen.getByRole('button', { name: 'Ponto de coleta — Ana' })).toBeInTheDocument(); }); it('removes an annotation when its pin is activated', () => { const onAnnotationRemove = vi.fn(); render( ); fireEvent.click(screen.getByRole('button', { name: 'Ponto' })); expect(onAnnotationRemove).toHaveBeenCalledWith('a1'); }); }); /* --- H2 ------------------------------------------------------------------ */ describe('device location', () => { it('renders the device position', () => { render(); expect(screen.getByRole('button', { name: 'Device location' })).toBeInTheDocument(); }); it('makes a simulated coordinate visible, not just logged', () => { render(); expect(screen.getByRole('button', { name: 'Simulated device location' })).toBeInTheDocument(); expect(document.querySelector('[data-map-caption]')).toHaveTextContent('Simulated location'); }); }); /* --- G1 ------------------------------------------------------------------ */ describe('time window', () => { const markers = [ { id: 'a', position: POA, title: 'Às 10h', data: { t: 600 } }, { id: 'b', position: { lat: -29, lng: -50 }, title: 'Às 14h', data: { t: 840 } }, { id: 'c', position: { lat: -28, lng: -49 }, title: 'Às 20h', data: { t: 1200 } }, ]; it('draws only the markers inside the window', () => { render(); expect(screen.getByRole('button', { name: 'Às 10h' })).toBeInTheDocument(); expect(screen.queryByRole('button', { name: 'Às 20h' })).not.toBeInTheDocument(); }); it('keeps records with no usable timestamp rather than vanishing them', () => { render( ); expect(screen.getByRole('button', { name: 'Sem carimbo' })).toBeInTheDocument(); }); it('counts only in-window records in a cluster badge', () => { render( 'all' }} /> ); // Filtering after aggregating would report 3. expect(screen.getByLabelText('2')).toBeInTheDocument(); }); }); /* --- A1 ------------------------------------------------------------------ */ describe('basemap', () => { it('filters only the tile pane, never the markers above it', () => { render(); const gmp = document.querySelector('gmp-map') as HTMLElement; expect(gmp.style.filter).toContain('invert'); // The marker must not inherit it. expect(screen.getByRole('button').style.filter).toBe(''); }); it('prefers a Cloud-styled mapId over the CSS fallback', () => { render( ); const gmp = document.querySelector('gmp-map') as HTMLElement; expect(gmp).toHaveAttribute('map-id', 'my-night-style'); expect(gmp.style.filter).toBe(''); }); it('uses the native map type for satellite', () => { render(); const gmp = document.querySelector('gmp-map') as any; expect(gmp.mapTypeId).toBe('satellite'); }); }); /* --- A4 / F5 -------------------------------------------------------------- */ it('renders consumer overlays as children', () => { render(
Legenda
); expect(screen.getByTestId('my-overlay')).toBeInTheDocument(); }); it('renders a scale bar when asked', () => { render(); expect(screen.getByText(/km|m$/)).toBeInTheDocument(); }); it('draws no scale bar by default', () => { render(); expect(screen.queryByText(/^\d+ (km|m)$/)).not.toBeInTheDocument(); }); });