import { FeatureGroup, Map as LMap, Marker, Popup, TileLayer } from 'leaflet'; import { describe, expect, it } from 'vitest'; import '../src/components/leaflet-map.ts'; import '../src/components/leaflet-tile-layer.ts'; import '../src/components/leaflet-feature-group.ts'; import '../src/components/leaflet-marker.ts'; import '../src/components/leaflet-popup.ts'; // Exercises the registration-bubbling protocol end to end across a small real // tree, rather than one component at a time: events bubble // from marker -> feature-group -> map, and popup binds to its parent marker // instead of bubbling past it. describe('component tree wiring', () => { it('wires a map + tile-layer + feature-group + marker + popup tree together', () => { const map = document.createElement('leaflet-map') as HTMLElement & { leafletObject?: LMap; }; map.setAttribute('lat', '51.5'); map.setAttribute('lng', '-0.09'); map.setAttribute('zoom', '13'); const tiles = document.createElement('leaflet-tile-layer') as HTMLElement & { leafletObject?: TileLayer; }; tiles.setAttribute('url', 'https://tile.example/{z}/{x}/{y}.png'); const group = document.createElement('leaflet-feature-group') as HTMLElement & { leafletObject?: FeatureGroup; }; const marker = document.createElement('leaflet-marker') as HTMLElement & { leafletObject?: Marker; }; marker.setAttribute('lat', '51.51'); marker.setAttribute('lng', '-0.1'); const popup = document.createElement('leaflet-popup') as HTMLElement & { leafletObject?: Popup; }; popup.innerHTML = 'hello'; marker.append(popup); group.append(marker); map.append(tiles, group); document.body.append(map); // tile-layer and feature-group both registered directly onto the map. expect(map.leafletObject?.hasLayer(tiles.leafletObject!)).toBe(true); expect(map.leafletObject?.hasLayer(group.leafletObject!)).toBe(true); // marker registered onto the feature-group (which, per Leaflet's own // LayerGroup.onAdd, also adds each child directly onto the map so it // renders -- hasLayer is true on both, membership is what distinguishes them). expect(group.leafletObject?.hasLayer(marker.leafletObject!)).toBe(true); expect(map.leafletObject?.hasLayer(marker.leafletObject!)).toBe(true); expect(group.leafletObject?.getLayers()).toEqual([marker.leafletObject]); // popup bound to the marker, not registered as a map layer at all. expect(marker.leafletObject?.getPopup()).toBe(popup.leafletObject); // A marker added later goes through the same path. const marker2 = document.createElement('leaflet-marker') as HTMLElement & { leafletObject?: Marker; }; marker2.setAttribute('lat', '1'); marker2.setAttribute('lng', '2'); group.append(marker2); expect(group.leafletObject?.hasLayer(marker2.leafletObject!)).toBe(true); // Disconnecting a child removes it from the map (WithProps calls the // Leaflet object's own .remove(), which detaches from its _map). // leafletObject is gone once disconnected, so capture the reference first. // Note this is a pre-existing Leaflet quirk, not something WithProps // introduces: Layer#remove() only detaches from the map, it does not tell // an owning LayerGroup/FeatureGroup to forget it (that needs the group's // own removeLayer()) -- so the group's hasLayer() stays stale/true here. const removedMarkerObj = marker.leafletObject!; marker.remove(); expect(map.leafletObject?.hasLayer(removedMarkerObj)).toBe(false); expect(group.leafletObject?.hasLayer(removedMarkerObj)).toBe(true); map.remove(); }); });