diff --git a/vehicle-data-platform/apps/web/src/pages/Quality.tsx b/vehicle-data-platform/apps/web/src/pages/Quality.tsx index 58e75635..0f8fc8a6 100644 --- a/vehicle-data-platform/apps/web/src/pages/Quality.tsx +++ b/vehicle-data-platform/apps/web/src/pages/Quality.tsx @@ -71,7 +71,7 @@ export function Quality({ onFiltersChange, initialFilters = {} }: { - onOpenVehicle: (vin: string) => void; + onOpenVehicle: (vin: string, protocol?: string) => void; onHealthLoaded?: (health: OpsHealth) => void; onFiltersChange?: (filters: Record) => void; initialFilters?: Record; @@ -239,7 +239,7 @@ export function Quality({ - diff --git a/vehicle-data-platform/apps/web/src/test/App.test.tsx b/vehicle-data-platform/apps/web/src/test/App.test.tsx index 1d6612a2..abf37326 100644 --- a/vehicle-data-platform/apps/web/src/test/App.test.tsx +++ b/vehicle-data-platform/apps/web/src/test/App.test.tsx @@ -997,6 +997,104 @@ test('renders quality issues as vehicle-service governance labels', async () => expect(screen.getAllByText('车辆服务').length).toBeGreaterThanOrEqual(1); }); +test('opens vehicle service from quality issue with issue source evidence', async () => { + window.history.replaceState(null, '', '/#/quality'); + vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => { + const path = String(input); + if (path.includes('/api/ops/health')) { + return { + ok: true, + json: async () => ({ + data: { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } }, + traceId: 'trace-test', + timestamp: 1783094400000 + }) + } as Response; + } + if (path.includes('/api/quality/summary')) { + return { + ok: true, + json: async () => ({ + data: { + issueVehicleCount: 1, + issueRecordCount: 1, + errorCount: 1, + warningCount: 0, + protocols: [{ name: 'JT808', count: 1 }], + issueTypes: [{ name: 'VIN_MISSING', count: 1 }] + }, + traceId: 'trace-test', + timestamp: 1783094400000 + }) + } as Response; + } + if (path.includes('/api/quality/issues')) { + return { + ok: true, + json: async () => ({ + data: { + items: [{ + vin: '', + plate: '粤AG18312', + phone: '13307795425', + sourceEndpoint: '115.231.168.135:43625', + protocol: 'JT808', + issueType: 'VIN_MISSING', + severity: 'error', + lastSeen: '2026-07-03 20:12:10', + detail: 'JT808 数据缺少 VIN 映射' + }], + total: 1, + limit: 20, + offset: 0 + }, + traceId: 'trace-test', + timestamp: 1783094400000 + }) + } as Response; + } + if (path.includes('/api/vehicle-service/overview')) { + return { + ok: true, + json: async () => ({ + data: { + vin: 'VIN-JT808-QUALITY', + plate: '粤AG18312', + sourceCount: 1, + onlineSourceCount: 1, + coverageStatus: 'online', + primaryProtocol: 'JT808', + lastSeen: '2026-07-03 20:12:10', + historyCount: 0, + rawCount: 0, + mileageCount: 0, + qualityIssueCount: 1 + }, + traceId: 'trace-test', + timestamp: 1783094400000 + }) + } as Response; + } + return { + ok: true, + json: async () => ({ + data: { items: [], total: 0, limit: 20, offset: 0 }, + traceId: 'trace-test', + timestamp: 1783094400000 + }) + } as Response; + }); + + render(); + + expect(await screen.findByText('13307795425')).toBeInTheDocument(); + fireEvent.click(screen.getByRole('button', { name: '按车牌查车' })); + + await waitFor(() => { + expect(window.location.hash).toBe('#/detail?keyword=VIN-JT808-QUALITY&protocol=JT808'); + }); +}); + test('drills into quality issues by issue type', async () => { window.history.replaceState(null, '', '/#/quality'); const fetchMock = vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {