import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { cleanup, render, renderHook, waitFor } from '@testing-library/react'; import { afterEach, expect, test, vi } from 'vitest'; import type { ReactNode } from 'react'; import { api } from '../../api/client'; import { useMonitorData, useMonitorVehicleCard } from './useMonitorData'; afterEach(() => { cleanup(); vi.restoreAllMocks(); }); test('rapid viewport changes retain one workspace payload and avoid duplicate fleet requests', async () => { const monitorSummary = vi.spyOn(api, 'monitorSummary').mockResolvedValue({ totalVehicles: 0, onlineVehicles: 0, offlineVehicles: 0, drivingVehicles: 0, idleVehicles: 0, unknownVehicles: 0, alertVehicles: 0, activeToday: 0, frameToday: 0, alertDataAvailable: false, truncated: false, asOf: '' }); const vehicleRealtime = vi.spyOn(api, 'vehicleRealtime').mockResolvedValue({ items: [], total: 0, limit: 200, offset: 0 }); const monitorWorkspace = vi.spyOn(api, 'monitorWorkspace').mockResolvedValue({ summary: { totalVehicles: 0, onlineVehicles: 0, offlineVehicles: 0, drivingVehicles: 0, idleVehicles: 0, unknownVehicles: 0, alertVehicles: 0, activeToday: 0, frameToday: 0, alertDataAvailable: false, truncated: false, asOf: '' }, vehicles: { items: [], total: 0, limit: 200, offset: 0 }, map: { mode: 'points', zoom: 13, total: 0, truncated: false, points: [], clusters: [], asOf: '' } }); const client = new QueryClient({ defaultOptions: { queries: { retry: false } } }); const wrapper = ({ children }: { children: ReactNode }) => {children}; const filters = { keyword: '', protocol: '', status: '' }; const view = renderHook(({ bounds }) => useMonitorData(filters, { zoom: 13, bounds }, '', true, false), { wrapper, initialProps: { bounds: '113.0000,22.0000,114.0000,23.0000' } }); await waitFor(() => expect(monitorWorkspace).toHaveBeenCalledTimes(1)); expect(monitorSummary).not.toHaveBeenCalled(); expect(vehicleRealtime).not.toHaveBeenCalled(); for (let index = 1; index <= 8; index += 1) { view.rerender({ bounds: `${113 + index / 1000},22,${114 + index / 1000},23` }); await waitFor(() => expect(monitorWorkspace.mock.calls.length).toBeGreaterThanOrEqual(index + 1)); } await waitFor(() => { const cachedWorkspaces = client.getQueryCache().findAll({ queryKey: ['monitor', 'workspace'] }); expect(cachedWorkspaces).toHaveLength(1); }); view.unmount(); client.clear(); }); test('repeated vehicle selections retain only the active realtime payload and release it on unmount', async () => { vi.spyOn(api, 'monitorSummary').mockResolvedValue({ totalVehicles: 0, onlineVehicles: 0, offlineVehicles: 0, drivingVehicles: 0, idleVehicles: 0, unknownVehicles: 0, alertVehicles: 0, activeToday: 0, frameToday: 0, alertDataAvailable: false, truncated: false, asOf: '' }); const vehicleRealtime = vi.spyOn(api, 'vehicleRealtime').mockResolvedValue({ items: [], total: 0, limit: 1, offset: 0 }); const client = new QueryClient({ defaultOptions: { queries: { retry: false } } }); const wrapper = ({ children }: { children: ReactNode }) => {children}; const filters = { keyword: '', protocol: '', status: '' }; const view = renderHook(({ vin }) => useMonitorData(filters, { zoom: 5, bounds: '' }, vin, false, false), { wrapper, initialProps: { vin: 'VIN-001' } }); await waitFor(() => expect(vehicleRealtime).toHaveBeenCalledTimes(1)); for (const vin of ['VIN-002', 'VIN-003', 'VIN-004']) { view.rerender({ vin }); await waitFor(() => expect(vehicleRealtime.mock.calls.length).toBeGreaterThanOrEqual(Number(vin.slice(-1)))); } await waitFor(() => expect(client.getQueryCache().findAll({ queryKey: ['monitor', 'selected-vehicle'] })).toHaveLength(1)); view.unmount(); await waitFor(() => expect(client.getQueryCache().findAll({ queryKey: ['monitor', 'selected-vehicle'] })).toHaveLength(0)); client.clear(); }); test('collapsed vehicle card does not issue or retain detail-only queries', async () => { const detail = vi.spyOn(api, 'vehicleDetail').mockResolvedValue({} as never); const alerts = vi.spyOn(api, 'alertEventsV2').mockResolvedValue({ items: [], total: 0, limit: 20, offset: 0 }); const address = vi.spyOn(api, 'reverseGeocode').mockResolvedValue({ provider: 'AMap', longitude: 113.26, latitude: 23.13, formattedAddress: '测试地址' }); const client = new QueryClient({ defaultOptions: { queries: { retry: false } } }); const vehicle = { vin: 'VIN-001', longitude: 113.26, latitude: 23.13 } as never; function ActiveCard() { useMonitorVehicleCard('VIN-001', vehicle, true); return null; } const view = render({null}); expect(detail).not.toHaveBeenCalled(); expect(alerts).not.toHaveBeenCalled(); expect(address).not.toHaveBeenCalled(); view.rerender(); await waitFor(() => expect(detail).toHaveBeenCalledTimes(1)); await waitFor(() => expect(alerts).toHaveBeenCalledTimes(1)); await waitFor(() => expect(address).toHaveBeenCalledTimes(1)); view.rerender({null}); await waitFor(() => expect(client.getQueryCache().findAll({ queryKey: ['monitor', 'vehicle-card'] })).toHaveLength(0)); view.unmount(); client.clear(); });