import { describe, it, expect, vi, beforeEach } from 'vitest' import type L from 'leaflet' import type { Map, LayerGroup } from 'leaflet' import { createCharacter, type CharacterData, type CharacterMapViewRef } from '../Character' const { leafletMock } = vi.hoisted(() => { const markerMock = { on: vi.fn().mockReturnThis(), addTo: vi.fn().mockReturnThis(), setLatLng: vi.fn().mockReturnThis(), setIcon: vi.fn().mockReturnThis(), } const Icon = vi.fn().mockImplementation(function (this: unknown) { return {} }) const L = { marker: vi.fn(() => markerMock), Icon, } return { leafletMock: L } }) vi.mock('leaflet', () => ({ __esModule: true, default: leafletMock, marker: leafletMock.marker, Icon: leafletMock.Icon, })) vi.mock('~/lib/LeafletCustomTypes', () => ({ HnHMaxZoom: 6, })) function getL(): L { return leafletMock as unknown as L } function makeCharData(overrides: Partial = {}): CharacterData { return { name: 'Hero', position: { x: 100, y: 200 }, type: 'player', id: 1, map: 1, ...overrides, } } function makeMapViewRef(mapid = 1): CharacterMapViewRef { return { map: { unproject: vi.fn(() => ({ lat: 0, lng: 0 })), removeLayer: vi.fn(), } as unknown as Map, mapid, markerLayer: { removeLayer: vi.fn(), addLayer: vi.fn(), } as unknown as LayerGroup, } } describe('createCharacter', () => { beforeEach(() => { vi.clearAllMocks() }) it('creates character with correct properties', () => { const char = createCharacter(makeCharData(), getL()) expect(char.id).toBe(1) expect(char.name).toBe('Hero') expect(char.position).toEqual({ x: 100, y: 200 }) expect(char.type).toBe('player') expect(char.map).toBe(1) expect(char.text).toBe('Hero') expect(char.value).toBe(1) }) it('starts with null leaflet marker', () => { const char = createCharacter(makeCharData(), getL()) expect(char.leafletMarker).toBeNull() }) it('add creates marker when character is on correct map', () => { const char = createCharacter(makeCharData(), getL()) const mapview = makeMapViewRef(1) char.add(mapview) expect(mapview.map.unproject).toHaveBeenCalled() }) it('add does not create marker for different map', () => { const char = createCharacter(makeCharData({ map: 2 }), getL()) const mapview = makeMapViewRef(1) char.add(mapview) expect(mapview.map.unproject).not.toHaveBeenCalled() }) it('update changes position and map', () => { const char = createCharacter(makeCharData(), getL()) const mapview = makeMapViewRef(1) char.update(mapview, { ...makeCharData(), position: { x: 300, y: 400 }, map: 2, }) expect(char.position).toEqual({ x: 300, y: 400 }) expect(char.map).toBe(2) }) it('remove on a character without leaflet marker does nothing', () => { const char = createCharacter(makeCharData(), getL()) const mapview = makeMapViewRef(1) char.remove(mapview) // should not throw expect(char.leafletMarker).toBeNull() }) it('setClickCallback works', () => { const char = createCharacter(makeCharData(), getL()) const cb = vi.fn() char.setClickCallback(cb) }) it('update with changed ownedByMe updates marker icon', () => { const char = createCharacter(makeCharData({ ownedByMe: false }), getL()) const mapview = makeMapViewRef(1) char.add(mapview) const marker = char.leafletMarker as { setIcon: ReturnType } expect(marker.setIcon).not.toHaveBeenCalled() char.update(mapview, makeCharData({ ownedByMe: true })) expect(marker.setIcon).toHaveBeenCalledTimes(1) }) })