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)
})
})