import { render, screen, fireEvent } from '@testing-library/react'; import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; import React from 'react'; /* * The whole point of this suite: the Maps script never loads. No API key, no * `google` global, no custom elements — exactly the blocked-network / exhausted * -quota situation the fallback exists for. */ vi.doMock('../google-maps-loader', () => ({ useGoogleMapsLoader: () => ({ isLoaded: false, loadError: undefined, load: async () => {}, }), })); const MUNICIPALITIES = [ { id: 'porto-alegre', position: { lat: -30.0346, lng: -51.2177 }, title: 'Porto Alegre' }, { id: 'caxias-do-sul', position: { lat: -29.1685, lng: -51.1796 }, title: 'Caxias do Sul' }, { id: 'pelotas', position: { lat: -31.7654, lng: -52.3376 }, title: 'Pelotas' }, ]; const OUTLINE = { type: 'FeatureCollection' as const, features: [ { type: 'Feature' as const, properties: { nome: 'RS' }, geometry: { type: 'Polygon', coordinates: [ [ [-53.11, -27.08], [-49.69, -27.08], [-49.69, -33.75], [-53.11, -33.75], [-53.11, -27.08], ], ], }, }, ], }; describe('Map without a basemap', () => { let Map: any; beforeEach(async () => { vi.clearAllMocks(); const module = await import('./map'); Map = module.Map; // jsdom reports zero-sized elements; the projection needs real pixels. Object.defineProperty(HTMLElement.prototype, 'clientWidth', { configurable: true, value: 800, }); Object.defineProperty(HTMLElement.prototype, 'clientHeight', { configurable: true, value: 600, }); }); afterEach(() => { vi.resetModules(); }); it('plots the markers when there is no API key at all', () => { render(); expect(screen.getByRole('button', { name: 'Porto Alegre' })).toBeInTheDocument(); expect(screen.getByRole('button', { name: 'Pelotas' })).toBeInTheDocument(); // No Google element was ever created. expect(document.querySelector('gmp-map')).not.toBeInTheDocument(); }); it('says the basemap is missing rather than passing for a real map', () => { render(); expect(screen.getByRole('status')).toHaveTextContent('Base cartográfica indisponível'); }); it('accepts a custom notice label', () => { render(); expect(screen.getByRole('status')).toHaveTextContent('Mapa offline'); }); it('places markers at distinct positions derived from their coordinates', () => { render(); const positions = screen.getAllByRole('button').map(button => { const host = button.closest('div[style*="left"]') as HTMLElement; return `${host.style.left}|${host.style.top}`; }); expect(new Set(positions).size).toBe(MUNICIPALITIES.length); }); it('puts the northern municipality above the southern one', () => { render(); const topOf = (name: string) => { const host = screen.getByRole('button', { name }).closest('div[style*="top"]') as HTMLElement; return parseFloat(host.style.top); }; // Caxias do Sul (-29.17) is north of Pelotas (-31.77). expect(topOf('Caxias do Sul')).toBeLessThan(topOf('Pelotas')); }); it('draws the reference outline when one is supplied', () => { const { container } = render(); const paths = container.querySelectorAll('svg path'); expect(paths.length).toBe(1); expect(paths[0].getAttribute('d')).toMatch(/^M[\d.]+ [\d.]+/); expect(paths[0].getAttribute('d')).toMatch(/Z$/); }); it('draws no outline when none is supplied', () => { const { container } = render(); expect(container.querySelector('svg path')).not.toBeInTheDocument(); }); it('keeps selection and keyboard activation working', () => { const onFeatureSelect = vi.fn(); render(); const target = screen.getByRole('button', { name: 'Pelotas' }); expect(target).toHaveAttribute('tabindex', '0'); fireEvent.keyDown(target, { key: 'Enter' }); expect(onFeatureSelect).toHaveBeenCalledWith( 'pelotas', expect.objectContaining({ id: 'pelotas', kind: 'marker' }) ); }); it('reflects a controlled selection', () => { render( {}} />); expect(screen.getByRole('button', { name: 'Pelotas' })).toHaveAttribute('aria-pressed', 'true'); }); it('still honours proportional sizing', () => { render( ); expect(screen.getByRole('button', { name: 'Grande' })).toHaveStyle({ width: '40px', height: '40px', }); }); it('renders the attribution', () => { render(); expect(screen.getByText('© IBGE')).toBeInTheDocument(); }); it('falls back to the setup prompt when the consumer opts out', () => { render(); expect(screen.getByText('Configure Google Maps API Key in Settings')).toBeInTheDocument(); expect(screen.queryByRole('button', { name: 'Porto Alegre' })).not.toBeInTheDocument(); }); it('lets the container states still outrank the fallback', () => { render(); expect(screen.getByText('No results for the current filters')).toBeInTheDocument(); expect(screen.queryByRole('button', { name: 'Porto Alegre' })).not.toBeInTheDocument(); }); });