mirror of
https://github.com/BigBodyCobain/Shadowbroker.git
synced 2026-08-09 20:26:03 +02:00
Merge pull request #488 from emorchy/feature/agent-fly-to-zoom
Feature/agent fly to zoom
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
/**
|
||||
* Agent fly_to zoom regression.
|
||||
*
|
||||
* `sar_focus_aoi` carries a zoom: a world-view revert asks for ~2, a tight
|
||||
* AOI for ~9. The zoom used to be dropped between the poll and the map, so
|
||||
* every agent move landed at a hardcoded zoom 8 — "show the whole world"
|
||||
* rendered as a patch of empty ocean. These tests pin the whole path:
|
||||
* the hook forwards zoom, the dashboard stores it, the viewer honors it.
|
||||
*/
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest';
|
||||
import { renderHook, waitFor } from '@testing-library/react';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
|
||||
import { useAgentActions } from '@/hooks/useAgentActions';
|
||||
|
||||
const SRC_DIR = path.resolve(__dirname, '../../');
|
||||
|
||||
function readSource(rel: string): string {
|
||||
return fs.readFileSync(path.join(SRC_DIR, rel), 'utf-8');
|
||||
}
|
||||
|
||||
function mockActions(actions: unknown[]) {
|
||||
return vi.fn().mockResolvedValue({
|
||||
ok: true,
|
||||
json: async () => ({ actions }),
|
||||
});
|
||||
}
|
||||
|
||||
describe('useAgentActions — fly_to zoom', () => {
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
vi.unstubAllGlobals();
|
||||
});
|
||||
|
||||
it('forwards the zoom the agent asked for', async () => {
|
||||
vi.stubGlobal(
|
||||
'fetch',
|
||||
mockActions([{ action: 'fly_to', lat: 20, lng: 0, zoom: 2, aoi_id: 'world' }]),
|
||||
);
|
||||
const onFlyTo = vi.fn();
|
||||
renderHook(() => useAgentActions(vi.fn(), onFlyTo));
|
||||
|
||||
await waitFor(() =>
|
||||
expect(onFlyTo).toHaveBeenCalledWith({ lat: 20, lng: 0, zoom: 2 }),
|
||||
);
|
||||
});
|
||||
|
||||
it('leaves zoom undefined when the action omits it', async () => {
|
||||
vi.stubGlobal('fetch', mockActions([{ action: 'fly_to', lat: 51.5, lng: -0.1 }]));
|
||||
const onFlyTo = vi.fn();
|
||||
renderHook(() => useAgentActions(vi.fn(), onFlyTo));
|
||||
|
||||
await waitFor(() =>
|
||||
expect(onFlyTo).toHaveBeenCalledWith({ lat: 51.5, lng: -0.1, zoom: undefined }),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('fly_to zoom survives the wiring', () => {
|
||||
it('the dashboard stores the agent zoom in flyToLocation', () => {
|
||||
const page = readSource('app/page.tsx');
|
||||
expect(page).toMatch(
|
||||
/useAgentActions\(\s*handleMapRightClick,\s*\(\{ lat, lng, zoom \}\) => \{\s*setFlyToLocation\(\{ lat, lng, zoom, ts: Date\.now\(\) \}\);/,
|
||||
);
|
||||
});
|
||||
|
||||
it('the viewer flies to the requested zoom, defaulting to 8', () => {
|
||||
const viewer = readSource('components/MaplibreViewer.tsx');
|
||||
expect(viewer).toContain('zoom: flyToLocation.zoom ?? 8,');
|
||||
expect(viewer).not.toMatch(/center: \[flyToLocation\.lng, flyToLocation\.lat\],\s*zoom: 8,/);
|
||||
});
|
||||
});
|
||||
@@ -436,6 +436,7 @@ export default function Dashboard() {
|
||||
const [flyToLocation, setFlyToLocation] = useState<{
|
||||
lat: number;
|
||||
lng: number;
|
||||
zoom?: number;
|
||||
ts: number;
|
||||
} | null>(null);
|
||||
|
||||
@@ -504,8 +505,8 @@ export default function Dashboard() {
|
||||
// Agent fly_to handler (sar_focus_aoi etc.) — wired here now that
|
||||
// setFlyToLocation is in scope. show_image is routed through
|
||||
// useAgentActions at the top of Dashboard.
|
||||
useAgentActions(handleMapRightClick, ({ lat, lng }) => {
|
||||
setFlyToLocation({ lat, lng, ts: Date.now() });
|
||||
useAgentActions(handleMapRightClick, ({ lat, lng, zoom }) => {
|
||||
setFlyToLocation({ lat, lng, zoom, ts: Date.now() });
|
||||
}, secondaryBootReady);
|
||||
|
||||
// Eavesdrop Mode State
|
||||
|
||||
@@ -752,9 +752,12 @@ const MaplibreViewer = ({
|
||||
|
||||
useEffect(() => {
|
||||
if (flyToLocation && mapRef.current) {
|
||||
// Agent moves (sar_focus_aoi) carry their own zoom: a world-view revert
|
||||
// asks for ~2, a tight AOI for ~9. Ignoring it pinned every move at 8,
|
||||
// which turned "show the whole world" into a patch of empty ocean.
|
||||
mapRef.current.flyTo({
|
||||
center: [flyToLocation.lng, flyToLocation.lat],
|
||||
zoom: 8,
|
||||
zoom: flyToLocation.zoom ?? 8,
|
||||
duration: 1500,
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user