mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-23 07:26:30 +00:00
1727732841
A new manifest capability poiCategories (up to 4, icon from an allow-list, strict #rrggbb color) and the hook poiCategoryProvider.getPois behind the hook:poi-category-provider permission. GET /api/plugin-pois in the plugin contributions module answers them in the /api/maps/pois shape, with search_plugin_pois as the MCP twin. plugin: ids no longer count as Google place ids. SDK 1.8.0 carries the types, validation and docs. On the map, one hook merges the built-in categories with the ones active plugins declare, and the pill shows the plugin chips after a divider on desktop and phone, scrolling sideways instead of shrinking a chip below its touch target. A plugin chip searches through a repo that refuses offline and parses the answer strictly. Its pins, on Leaflet and GL, use the category's own icon and color, both checked again before they reach any markup, and the hover card lists the plugin's details as escaped text. A plugin POI can be added as a place like any other. The admin panels on desktop and phone show a chip for the new permission and the categories a plugin would add, from one shared module. Addresses discussion #1781.
98 lines
4.3 KiB
TypeScript
98 lines
4.3 KiB
TypeScript
import { describe, it, expect } from 'vitest'
|
|
import { renderHook } from '@testing-library/react'
|
|
import { useMergedMapPois } from './useMergedMapPois'
|
|
import type { Poi } from './poiCategories'
|
|
|
|
/**
|
|
* FE-MAP-MERGEPOI-001..010: the two searches that can run at once in road trip mode.
|
|
*
|
|
* The corridor answers "what is along the drive", the category pill answers "what is in
|
|
* view". Both are drawn, the corridor's copy of a shared hit wins, and the reference is
|
|
* only new when the content really is — the map rebuilds every pin off it.
|
|
*/
|
|
|
|
const poi = (osm_id: string, name = osm_id): Poi => ({
|
|
osm_id, name, lat: 48, lng: 2, category: 'hotel', source: 'openstreetmap',
|
|
} as Poi)
|
|
|
|
describe('useMergedMapPois', () => {
|
|
it('FE-MAP-MERGEPOI-001: outside road trip mode only the pill hits are drawn', () => {
|
|
const explore = [poi('n1')]
|
|
const { result } = renderHook(() => useMergedMapPois(null, explore))
|
|
expect(result.current).toBe(explore)
|
|
})
|
|
|
|
it('FE-MAP-MERGEPOI-002: with nothing found in view the corridor list passes through as-is', () => {
|
|
const corridor = [poi('n1')]
|
|
const { result } = renderHook(() => useMergedMapPois(corridor, []))
|
|
expect(result.current).toBe(corridor)
|
|
})
|
|
|
|
it('FE-MAP-MERGEPOI-003: before a corridor search the pill hits pass through as-is', () => {
|
|
const explore = [poi('n2')]
|
|
const { result } = renderHook(() => useMergedMapPois([], explore))
|
|
expect(result.current).toBe(explore)
|
|
})
|
|
|
|
it('FE-MAP-MERGEPOI-004: both searches draw together, corridor first', () => {
|
|
const { result } = renderHook(() => useMergedMapPois([poi('n1')], [poi('n2')]))
|
|
expect(result.current.map(p => p.osm_id)).toEqual(['n1', 'n2'])
|
|
})
|
|
|
|
it('FE-MAP-MERGEPOI-005: a place both searches found is drawn once, as the corridor knows it', () => {
|
|
// The corridor copy carries how far along the drive it sits, which the pill's does not.
|
|
const corridor = [poi('n1', 'Hotel am Weg')]
|
|
const { result } = renderHook(() => useMergedMapPois(corridor, [poi('n1', 'Hotel am Weg (Sicht)')]))
|
|
expect(result.current).toBe(corridor)
|
|
expect(result.current).toHaveLength(1)
|
|
expect(result.current[0].name).toBe('Hotel am Weg')
|
|
})
|
|
|
|
it('FE-MAP-MERGEPOI-006: an unchanged pair keeps its reference, so no pin is rebuilt', () => {
|
|
const corridor = [poi('n1')]
|
|
const explore = [poi('n2')]
|
|
const { result, rerender } = renderHook(() => useMergedMapPois(corridor, explore))
|
|
const first = result.current
|
|
rerender()
|
|
expect(result.current).toBe(first)
|
|
})
|
|
})
|
|
|
|
describe('useMergedMapPois — the refuel offers', () => {
|
|
const poi = (id: string): Poi => ({ osm_id: id, name: id, lat: 0, lng: 0 } as Poi)
|
|
|
|
it('FE-MAP-MERGEPOI-007: an offered station is drawn even with no search running', () => {
|
|
// In road trip mode this is the only channel a POI reaches the map through, so
|
|
// without it somebody is asked to accept a stop they cannot see.
|
|
const { result } = renderHook(() => useMergedMapPois([], [], [poi('offer')]))
|
|
expect(result.current.map(p => p.osm_id)).toEqual(['offer'])
|
|
})
|
|
|
|
it('FE-MAP-MERGEPOI-008: an offer already on the route is one pin, not two', () => {
|
|
const { result } = renderHook(() => useMergedMapPois([poi('a')], [], [poi('a'), poi('b')]))
|
|
expect(result.current.map(p => p.osm_id)).toEqual(['a', 'b'])
|
|
})
|
|
|
|
it('FE-MAP-MERGEPOI-009: nothing offered keeps the same array, so no pin is rebuilt', () => {
|
|
const corridor = [poi('a')]
|
|
const { result, rerender } = renderHook(({ o }: { o: Poi[] }) => useMergedMapPois(corridor, [], o), {
|
|
initialProps: { o: [] as Poi[] },
|
|
})
|
|
const first = result.current
|
|
rerender({ o: [] })
|
|
expect(result.current).toBe(first)
|
|
expect(result.current).toBe(corridor)
|
|
})
|
|
|
|
it('FE-MAP-MERGEPOI-010: a plugin POI next to the corridor is its own pin and keeps its look and rows', () => {
|
|
// Namespaced by the host, so a plugin id never collides with an OSM element of the same number.
|
|
const trailhead = {
|
|
...poi('plugin:trail-finder:1'), category: 'plugin:trail-finder/trailheads',
|
|
icon: 'Signpost', color: '#2f855a', details: [{ label: 'Length', value: '12 km' }],
|
|
}
|
|
const { result } = renderHook(() => useMergedMapPois([poi('node:1')], [trailhead]))
|
|
expect(result.current.map(p => p.osm_id)).toEqual(['node:1', 'plugin:trail-finder:1'])
|
|
expect(result.current[1]).toBe(trailhead)
|
|
})
|
|
})
|