import { describe, expect, test, vi, beforeEach } from 'vitest' import { render, within, fireEvent } from '@testing-library/react' import { GeolocationControls } from './geolocation-controls' describe('GeolocationControls', () => { const mockOnChange = vi.fn() const mockOnError = vi.fn() const mockGeolocation = { getCurrentPosition: vi.fn(), watchPosition: vi.fn(), clearWatch: vi.fn(), } const mockPermissions = { query: vi.fn(), state: 'granted' as const, onchange: null as (() => void) | null, } const mockNavigator = { geolocation: mockGeolocation, permissions: mockPermissions, } beforeEach(() => { vi.clearAllMocks() // Mock navigator Object.defineProperty(global, 'navigator', { value: mockNavigator, writable: true, }) }) test('renders correctly with geolocation enabled', () => { mockPermissions.query.mockResolvedValue(mockPermissions) const { container } = render( , ) const button = within(container).getByRole('button') const buttonElement = button as unknown as HTMLButtonElement expect(buttonElement.disabled).toBe(false) }) test('handles geolocation permission denied', () => { mockPermissions.query.mockResolvedValue({ ...mockPermissions, state: 'denied', }) const { container } = render( , ) const button = within(container).getByRole('button') const buttonElement = button as unknown as HTMLButtonElement expect(buttonElement.disabled).toBe(false) }) test('calls getCurrentPosition when clicked', () => { mockPermissions.query.mockResolvedValue(mockPermissions) const mockPosition = { coords: { latitude: 40.7128, longitude: -74.006, }, } mockGeolocation.getCurrentPosition.mockImplementation( (success: PositionCallback) => success(mockPosition as GeolocationPosition), ) const { container } = render( , ) const button = within(container).getByRole('button') fireEvent.click(button) expect(mockGeolocation.getCurrentPosition).toHaveBeenCalled() expect(mockOnChange).toHaveBeenCalledWith(mockPosition.coords) }) test('calls watchPosition when watch prop is true', () => { mockPermissions.query.mockResolvedValue(mockPermissions) const mockPosition = { coords: { latitude: 40.7128, longitude: -74.006, }, } mockGeolocation.watchPosition.mockImplementation( (success: PositionCallback) => success(mockPosition as GeolocationPosition), ) const { container } = render( , ) const button = within(container).getByRole('button') fireEvent.click(button) expect(mockGeolocation.watchPosition).toHaveBeenCalled() expect(mockOnChange).toHaveBeenCalledWith(mockPosition.coords) }) test('does not re-query permissions when only the onError identity changes', () => { mockPermissions.query.mockResolvedValue(mockPermissions) const { rerender } = render( , ) expect(mockPermissions.query).toHaveBeenCalledTimes(1) // `onError` is read through an Effect Event, so the permission-query // effect depends on `hasGeolocation` alone. A fresh callback identity // must NOT re-run the query or re-register the permission listener. rerender() expect(mockPermissions.query).toHaveBeenCalledTimes(1) }) test('handles geolocation errors', () => { mockPermissions.query.mockResolvedValue(mockPermissions) const mockError = new Error('Geolocation error') mockGeolocation.getCurrentPosition.mockImplementation( (_, error: PositionErrorCallback) => { error(mockError as unknown as GeolocationPositionError) }, ) const { container } = render( , ) const button = within(container).getByRole('button') fireEvent.click(button) expect(mockOnError).toHaveBeenCalledWith(mockError) }) })