feat(platform): harden telemetry pipeline and unify Semi UI workspaces

This commit is contained in:
lingniu
2026-07-18 00:26:36 +08:00
parent 65b4e4f055
commit 159c80b0ae
136 changed files with 21616 additions and 1785 deletions

View File

@@ -52,7 +52,17 @@ test('renders only the desktop matrix with dates as columns and a period total',
expect(screen.queryByText('车辆里程排名')).not.toBeInTheDocument();
expect(screen.getByText('已绑定主车辆')).toBeInTheDocument();
expect(screen.getByText('已选择 1 辆')).toBeInTheDocument();
expect(screen.getByText('已绑定主车辆').closest('.semi-card')).toHaveClass('v2-mileage-summary-card');
expect(screen.getByText('车辆每日里程').closest('.semi-card')).toHaveClass('v2-mileage-results');
expect(view.container.querySelector('.v2-mileage-query-panel.semi-card')).toBeInTheDocument();
expect(view.container.querySelector('.v2-mileage-page > .v2-page-heading')).not.toBeInTheDocument();
expect(view.container.querySelector('.v2-mileage-summary.semi-card')).toHaveAttribute('aria-label', '里程查询统计信息');
expect(view.container.querySelector('.v2-mileage-summary-secondary.semi-card-group')).toBeInTheDocument();
expect(view.container.querySelectorAll('.v2-mileage-summary-card.semi-card')).toHaveLength(4);
expect(view.container.querySelector('.v2-mileage-summary-card.is-primary .v2-mileage-summary-value')).toHaveTextContent('193.3 km');
expect(view.container.querySelector('.v2-mileage-table-wrap')).toBeInTheDocument();
expect(view.container.querySelector('.v2-mileage-table.semi-table-wrapper')).toBeInTheDocument();
expect(view.container.querySelector('.v2-mileage-table-wrap > table')).not.toBeInTheDocument();
expect(view.container.querySelector('.v2-mileage-mobile-list')).not.toBeInTheDocument();
await waitFor(() => expect(mocks.dailyMileage).toHaveBeenCalledTimes(1));
expect(mocks.dailyMileage.mock.calls[0][0].get('limit')).toBe('10000');
@@ -127,7 +137,10 @@ test('removes the previous mileage scope while a new date range is loading', asy
expect(screen.queryByText('193.3 km')).not.toBeInTheDocument();
expect(screen.queryByText('104.6 km')).not.toBeInTheDocument();
expect(screen.queryByText('88.7 km')).not.toBeInTheDocument();
expect(screen.getAllByText('2026-07-10 至 2026-07-16').length).toBeGreaterThan(0);
const end = new Date();
const start = new Date(Date.now() - 6 * 86_400_000);
const localDate = (value: Date) => new Date(value.getTime() - value.getTimezoneOffset() * 60_000).toISOString().slice(0, 10);
expect(screen.getAllByText(`${localDate(start)}${localDate(end)}`).length).toBeGreaterThan(0);
});
test('uses one responsive mileage matrix without viewport listeners', async () => {
@@ -137,26 +150,67 @@ test('uses one responsive mileage matrix without viewport listeners', async () =
expect(await screen.findByText('车辆每日里程')).toBeInTheDocument();
await waitFor(() => expect(view.container.querySelectorAll('.v2-mileage-table tbody tr')).toHaveLength(1));
expect(view.container.querySelector('.v2-mileage-table-wrap')).toBeInTheDocument();
expect(view.container.querySelector('.v2-mileage-table.semi-table-wrapper')).toBeInTheDocument();
expect(view.container.querySelectorAll('.v2-mileage-table th.is-date')).toHaveLength(2);
expect(view.container.querySelector('.v2-mileage-table th.is-plate')).toHaveTextContent('车牌');
expect(view.container.querySelector('.v2-mileage-table th.is-total')).toHaveTextContent('区间总里程');
expect(view.container.querySelector('.v2-mileage-table th.is-vin')).not.toBeInTheDocument();
expect(view.container.querySelector('.v2-mileage-table th.is-plate')).toHaveClass('is-plate');
expect(view.container.querySelector('.v2-mileage-table th.is-total')).toHaveClass('is-total');
expect(view.container.querySelector('.v2-mileage-mobile-list')).not.toBeInTheDocument();
});
test('routes vertical wheel input from the mileage workspace to the table without hijacking buttons', async () => {
prepareData();
const view = renderPage('/statistics?vins=LTEST000000000001&dateFrom=2026-07-13&dateTo=2026-07-14');
expect(await screen.findByText('车辆每日里程')).toBeInTheDocument();
const page = view.container.querySelector<HTMLElement>('.v2-mileage-page');
expect(page).toBeInTheDocument();
await waitFor(() => expect(view.container.querySelector('.v2-mileage-table-wrap')).toBeInTheDocument());
const scroller = view.container.querySelector<HTMLElement>('.v2-mileage-table-wrap');
expect(scroller).toBeInTheDocument();
Object.defineProperties(scroller!, {
clientHeight: { configurable: true, value: 300 },
scrollHeight: { configurable: true, value: 900 },
scrollTop: { configurable: true, value: 0, writable: true }
});
fireEvent.wheel(page!, { deltaY: 180, deltaX: 0 });
expect(scroller!.scrollTop).toBe(180);
fireEvent.wheel(screen.getByRole('button', { name: '刷新里程数据' }), { deltaY: 180, deltaX: 0 });
expect(scroller!.scrollTop).toBe(180);
fireEvent.wheel(page!, { deltaY: 120, deltaX: 240 });
expect(scroller!.scrollTop).toBe(180);
});
test('supports searching and selecting license plates before querying exact VINs', async () => {
prepareData();
mocks.vehicles.mockResolvedValue({
items: [
{ vin: 'LTEST000000000001', plate: '粤A12345', protocol: 'GB32960' },
{ vin: 'LTEST000000000001', plate: '粤A12345', protocol: 'JT808' }
],
total: 2,
limit: 12,
offset: 0
});
renderPage();
const search = screen.getByRole('textbox', { name: '搜索车牌' });
fireEvent.focus(search);
fireEvent.change(search, { target: { value: '粤A12' } });
expect(await screen.findByRole('option', { name: /粤A12345/ })).toBeInTheDocument();
fireEvent.click(screen.getByRole('option', { name: /粤A12345/ }));
const option = await screen.findByRole('option', { name: '粤A12345 LTEST000000000001 GB32960 JT808 选择' });
expect(screen.getAllByRole('option')).toHaveLength(1);
fireEvent.click(option);
fireEvent.click(screen.getByRole('button', { name: '查询' }));
await waitFor(() => {
const calls = mocks.mileageStatistics.mock.calls;
expect(calls[calls.length - 1]?.[0].get('vins')).toBe('LTEST000000000001');
});
expect(screen.getByTitle('LTEST000000000001')).toHaveTextContent('粤A12345');
expect(document.querySelector('.v2-mileage-chip')).toHaveClass('semi-tag');
expect(document.querySelector('.v2-mileage-chip')).toHaveTextContent('粤A12345');
});
test('shows and recovers from a failed license plate candidate query', async () => {
@@ -179,8 +233,12 @@ test('lets users disable mileage sources and persists the source priority', asyn
renderPage('/statistics?vins=LTEST000000000001&dateFrom=2026-07-13&dateTo=2026-07-14');
fireEvent.click(screen.getByRole('button', { name: /数据源/ }));
expect(screen.getByRole('dialog', { name: '数据源策略配置' })).toBeInTheDocument();
expect(document.querySelectorAll('.v2-mileage-source-card.semi-card')).toHaveLength(3);
expect(screen.getByText('优先级 1').closest('.semi-tag')).toHaveClass('v2-mileage-source-priority');
expect(screen.getByText('GPS 里程')).toBeInTheDocument();
expect(screen.getByText('GPS 里程').closest('.semi-tag')).toBeInTheDocument();
expect(screen.getAllByText('仪表盘里程')).toHaveLength(2);
expect(screen.getByRole('button', { name: /数据源.*GB32960 优先.*3\/3/ })).toHaveAttribute('title', '当前优先:国标 GB32960');
fireEvent.click(screen.getByRole('switch', { name: '禁用 国标 GB32960' }));
fireEvent.click(screen.getByRole('button', { name: '上移 宇通 MQTT' }));
fireEvent.click(screen.getByRole('button', { name: '查询' }));
@@ -206,14 +264,16 @@ test('paginates all unique vehicles when no license plate is selected', async ()
renderPage('/statistics?dateFrom=2026-07-13&dateTo=2026-07-14');
expect((await screen.findAllByText('粤A00001')).length).toBeGreaterThan(0);
expect(screen.getByText('第 1 / 2 页 · 共 32 辆 · 每页 20 辆')).toBeInTheDocument();
expect(screen.getByText('共 32 辆 · 每页 20 辆')).toBeInTheDocument();
expect(document.querySelector('.v2-table-pagination-current')).toHaveTextContent('1/2');
expect(screen.getByText('档案口径 · 1 辆有里程')).toBeInTheDocument();
expect(mocks.vehicleCoverage.mock.calls[0][0].get('bindingStatus')).toBe('bound');
await waitFor(() => expect(mocks.dailyMileage.mock.calls[mocks.dailyMileage.mock.calls.length - 1]?.[0].get('vins')).toContain('VIN00000000000001'));
fireEvent.click(screen.getByRole('button', { name: '下一页' }));
expect((await screen.findAllByText('粤A00021')).length).toBeGreaterThan(0);
expect(screen.getByText('第 2 / 2 页 · 共 32 辆 · 每页 20 辆')).toBeInTheDocument();
expect(screen.getByText('共 32 辆 · 每页 20 辆')).toBeInTheDocument();
expect(document.querySelector('.v2-table-pagination-current')).toHaveTextContent('2/2');
await waitFor(() => expect(mocks.vehicleCoverage.mock.calls[mocks.vehicleCoverage.mock.calls.length - 1]?.[0].get('offset')).toBe('20'));
});