perf(web): release track map listeners explicitly

This commit is contained in:
lingniu
2026-07-16 01:32:55 +08:00
parent 8ebffa4fee
commit 639564b96d
3 changed files with 148 additions and 4 deletions

View File

@@ -0,0 +1,117 @@
import { cleanup, render, waitFor } from '@testing-library/react';
import { afterEach, expect, test, vi } from 'vitest';
import type { HistoryLocationRow, TrackStop } from '../../api/types';
import type { AMapLike } from '../../integrations/amap';
import { TrackMap } from './TrackMap';
const overlayOff = vi.fn();
const overlaySetMap = vi.fn();
const mapOff = vi.fn();
const mapDestroy = vi.fn();
const markerListeners: Array<{ overlay: TestOverlay; event: string; handler: () => void }> = [];
class TestOverlay {
on = vi.fn((event: string, handler: () => void) => markerListeners.push({ overlay: this, event, handler }));
off = overlayOff;
setMap = overlaySetMap;
setPosition = vi.fn();
setPath = vi.fn();
}
class TestMap {
add = vi.fn();
addControl = vi.fn();
setFitView = vi.fn();
destroy = mapDestroy;
on = vi.fn();
off = mapOff;
panTo = vi.fn();
}
class TestScale {}
class TestToolBar {}
class TestSize {}
class TestPixel {}
class TestMassMarks {}
function amapMock(): AMapLike {
return {
Map: TestMap as unknown as AMapLike['Map'],
Marker: TestOverlay as unknown as AMapLike['Marker'],
Polyline: TestOverlay as unknown as AMapLike['Polyline'],
Scale: TestScale,
ToolBar: TestToolBar,
Size: TestSize as unknown as AMapLike['Size'],
Pixel: TestPixel as unknown as AMapLike['Pixel'],
MassMarks: TestMassMarks as unknown as AMapLike['MassMarks']
};
}
const points = [{
vin: 'LTEST000000000001',
plate: '粤A12345',
protocol: 'JT808',
deviceTime: '2026-07-16T00:00:00Z',
serverTime: '2026-07-16T00:00:01Z',
longitude: 113.26,
latitude: 23.13,
speedKmh: 0,
totalMileageKm: 100
}, {
vin: 'LTEST000000000001',
plate: '粤A12345',
protocol: 'JT808',
deviceTime: '2026-07-16T00:10:00Z',
serverTime: '2026-07-16T00:10:01Z',
longitude: 113.28,
latitude: 23.15,
speedKmh: 30,
totalMileageKm: 105
}] as HistoryLocationRow[];
const stops = [{
index: 0,
startTime: '2026-07-16T00:00:00Z',
endTime: '2026-07-16T00:05:00Z',
durationSeconds: 300,
pointCount: 1,
longitude: 113.26,
latitude: 23.13,
sampledIndex: 0,
evidence: 'GPS 推断'
}] as TrackStop[];
afterEach(() => {
cleanup();
delete window.__LINGNIU_APP_CONFIG__;
delete window.AMapLoader;
overlayOff.mockReset();
overlaySetMap.mockReset();
mapOff.mockReset();
mapDestroy.mockReset();
markerListeners.length = 0;
});
test('detaches every marker listener and map listener on unmount', async () => {
window.__LINGNIU_APP_CONFIG__ = { amapWebJsKey: 'amap-web-key' };
window.AMapLoader = { load: vi.fn(async () => amapMock()) };
const view = render(<TrackMap
points={points}
stops={stops}
activeIndex={0}
showStops
follow
onSelectIndex={() => undefined}
onFollowChange={() => undefined}
/>);
await waitFor(() => expect(markerListeners).toHaveLength(3));
view.unmount();
for (const listener of markerListeners) {
expect(overlayOff).toHaveBeenCalledWith(listener.event, listener.handler);
}
expect(mapOff).toHaveBeenCalledWith('dragstart', expect.any(Function));
expect(overlaySetMap).toHaveBeenCalled();
expect(mapDestroy).toHaveBeenCalledTimes(1);
});