#49 feat: agrupacion de marcadores del mismo punto con contador y popup de productos en el mapa de provenance

This commit is contained in:
2026-08-16 12:24:49 -05:00
parent 6a833a1ea4
commit f24e7ab2ec
3 changed files with 180 additions and 22 deletions

View File

@@ -15,6 +15,7 @@ const leaflet = vi.hoisted(() => {
addTo: vi.fn(function () { return this }),
on: vi.fn(),
bindTooltip: vi.fn(function () { return this }),
bindPopup: vi.fn(function () { return this }),
remove: vi.fn(),
getLatLng: vi.fn(function () { return this.pos }),
setLatLng: vi.fn(),
@@ -85,6 +86,17 @@ const provenance = [
},
]
const sameMuni = [
{
product: { id: 1, name: 'Panela', catalogue_images: [] },
suppliers: [{ supplier: { id: 5, name: 'A' }, municipality: { id: 7, name: 'LA MESA', latitude: '4.6310280', longitude: '-74.4615880' } }],
},
{
product: { id: 2, name: 'Arroz', catalogue_images: [] },
suppliers: [{ supplier: { id: 6, name: 'B' }, municipality: { id: 7, name: 'LA MESA', latitude: '4.6310280', longitude: '-74.4615880' } }],
},
]
function mountMap (props = {}) {
const api = { getStoreSettings: vi.fn().mockResolvedValue(settings) }
const pinia = createPinia()
@@ -202,26 +214,68 @@ describe('ProvenanceMap', () => {
})
it('mantiene la posición exacta del municipio aunque varios productos coincidan', async () => {
const sameMuni = [
{
product: { id: 1, name: 'Panela', catalogue_images: [] },
suppliers: [{ supplier: { id: 5, name: 'A' }, municipality: { id: 7, name: 'LA MESA', latitude: '4.6310280', longitude: '-74.4615880' } }],
},
{
product: { id: 2, name: 'Arroz', catalogue_images: [] },
suppliers: [{ supplier: { id: 6, name: 'B' }, municipality: { id: 7, name: 'LA MESA', latitude: '4.6310280', longitude: '-74.4615880' } }],
},
]
mountMap({ provenance: sameMuni })
await flushPromises()
const productPositions = leaflet.markers.slice(1).map(marker => marker.pos)
expect(productPositions).toHaveLength(2)
expect(productPositions[0]).toEqual([4.631028, -74.461588])
expect(productPositions[1]).toEqual([4.631028, -74.461588])
expect(leaflet.markers).toHaveLength(2)
expect(leaflet.markers[1].pos).toEqual([4.631028, -74.461588])
expect(leaflet.map.on).not.toHaveBeenCalledWith('zoomend', expect.anything())
})
it('agrupa los productos del mismo punto en un marcador con contador', async () => {
mountMap({ provenance: sameMuni })
await flushPromises()
const groupIcon = leaflet.L.divIcon.mock.calls.find(args => args[0].className === 'provenance-group-pin')
expect(groupIcon).toBeDefined()
expect(groupIcon[0].html).toContain('2')
expect(groupIcon[0].html).toContain('mdi-package-variant')
const groupMarker = leaflet.markers[1]
expect(groupMarker.bindPopup).toHaveBeenCalled()
expect(groupMarker.bindTooltip.mock.calls[0][0]).toContain('2 productos')
})
it('al hacer clic en el marcador agrupado despliega los productos internos', async () => {
mountMap({ provenance: sameMuni })
await flushPromises()
const popupEl = leaflet.markers[1].bindPopup.mock.calls[0][0]
expect(popupEl.textContent).toContain('Panela')
expect(popupEl.textContent).toContain('Arroz')
})
it('al hacer clic en un producto del popup agrupado abre el diálogo', async () => {
mountMap({ provenance: sameMuni })
await flushPromises()
const popupEl = leaflet.markers[1].bindPopup.mock.calls[0][0]
const item = popupEl.querySelector('[data-test="map-popup-item-1"]')
item.dispatchEvent(new Event('click'))
await nextTick()
expect(document.body.textContent).toContain('Arroz')
expect(document.body.textContent).toContain('B')
})
it('al pasar sobre la tienda dibuja una línea por cada producto agrupado', async () => {
mountMap({ provenance: sameMuni })
await flushPromises()
handlerOf(leaflet.markers[0], 'mouseover')()
expect(leaflet.L.polyline).toHaveBeenCalledTimes(2)
})
it('al pasar sobre el marcador agrupado dibuja las líneas de sus productos', async () => {
mountMap({ provenance: sameMuni })
await flushPromises()
handlerOf(leaflet.markers[1], 'mouseover')()
expect(leaflet.L.polyline).toHaveBeenCalledTimes(2)
expect(leaflet.polylines[0].points[0]).toEqual([4.631028, -74.461588])
})
it('muestra un recuadro con todos los productos, incluidos los sin geolocalización', async () => {
const withNoGeo = [
{