feat(platform): harden telemetry pipeline and unify Semi UI workspaces
This commit is contained in:
@@ -9,11 +9,14 @@ const mocks = vi.hoisted(() => ({
|
||||
createCustomerUser: vi.fn(),
|
||||
updateCustomerUser: vi.fn()
|
||||
}));
|
||||
const layout = vi.hoisted(() => ({ mobile: false }));
|
||||
|
||||
vi.mock('../../api/client', () => ({ api: mocks }));
|
||||
vi.mock('../hooks/useMobileLayout', () => ({ useMobileLayout: () => layout.mobile }));
|
||||
|
||||
afterEach(() => {
|
||||
cleanup();
|
||||
layout.mobile = false;
|
||||
Object.values(mocks).forEach((mock) => mock.mockReset());
|
||||
});
|
||||
|
||||
@@ -38,19 +41,129 @@ test('deduplicates vehicle candidates by VIN and renders granted vehicles plate
|
||||
mocks.vehicleCoverage.mockResolvedValue({ items: [duplicatedVehicle, duplicatedVehicle], total: 1, limit: 20, offset: 0 });
|
||||
const client = new QueryClient({ defaultOptions: { queries: { retry: false } } });
|
||||
|
||||
render(<QueryClientProvider client={client}><UsersPage /></QueryClientProvider>);
|
||||
const view = render(<QueryClientProvider client={client}><UsersPage /></QueryClientProvider>);
|
||||
|
||||
fireEvent.click(await screen.findByRole('button', { name: /华东客户/ }));
|
||||
expect((await screen.findByRole('button', { name: '移除 粤A11111' })).closest('article')).toHaveTextContent('粤A11111VIN001');
|
||||
const customer = await screen.findByRole('button', { name: /选择客户 华东客户/ });
|
||||
expect(view.container.querySelector('.v2-user-list.semi-card')).toBeInTheDocument();
|
||||
expect(view.container.querySelector('.v2-customer-list.semi-list')).toBeInTheDocument();
|
||||
expect(customer).toHaveClass('semi-button', 'v2-user-list-item');
|
||||
expect(customer.closest('.semi-list-item')).toHaveClass('v2-user-list-row');
|
||||
expect(customer).toHaveAttribute('aria-pressed', 'false');
|
||||
expect(customer).toHaveAttribute('aria-expanded', 'false');
|
||||
expect(view.container.querySelector('.v2-user-editor.semi-card')).not.toBeInTheDocument();
|
||||
fireEvent.click(customer);
|
||||
await waitFor(() => expect(customer).toHaveAttribute('aria-expanded', 'true'));
|
||||
expect(view.container.querySelector('.v2-user-editor.semi-card')).toBeInTheDocument();
|
||||
expect(view.container.querySelector('.v2-user-editor-tabs.semi-tabs')).toBeInTheDocument();
|
||||
expect(screen.getAllByRole('tab')).toHaveLength(3);
|
||||
expect(view.container.querySelector('.v2-user-vehicle-section.semi-card')).toBeInTheDocument();
|
||||
fireEvent.click(screen.getByRole('tab', { name: /菜单权限/ }));
|
||||
expect(screen.getByText('客户开放菜单').closest('.semi-tag')).toBeInTheDocument();
|
||||
fireEvent.click(screen.getByRole('tab', { name: /车辆权限/ }));
|
||||
expect(document.querySelector('.v2-user-vehicle-section .v2-user-section-title .semi-tag')).toHaveTextContent('1 辆');
|
||||
const granted = await screen.findByRole('button', { name: '移除 粤A11111' });
|
||||
expect(granted).toHaveClass('semi-button');
|
||||
expect(granted.closest('.v2-assigned-vehicle-card')).toHaveClass('semi-card');
|
||||
expect(granted.closest('.v2-assigned-vehicle-card')).toHaveTextContent('粤A11111VIN001');
|
||||
expect(screen.getByRole('button', { name: '调整 粤A11111 有效期' })).toHaveAttribute('aria-haspopup', 'dialog');
|
||||
expect(document.querySelectorAll('input[type="datetime-local"]')).toHaveLength(0);
|
||||
fireEvent.click(screen.getByRole('button', { name: '调整 粤A11111 有效期' }));
|
||||
expect(await screen.findByLabelText('粤A11111 启用时间')).toHaveValue('2026-06-05T00:00');
|
||||
expect(document.querySelectorAll('input[type="datetime-local"]')).toHaveLength(2);
|
||||
expect(document.querySelector('.v2-user-grant-sidesheet .semi-sidesheet-inner')).toHaveAttribute('aria-label', '车辆授权有效期');
|
||||
fireEvent.click(screen.getByRole('button', { name: '关闭车辆授权有效期' }));
|
||||
expect(document.querySelectorAll('input[type="datetime-local"]')).toHaveLength(0);
|
||||
expect(document.querySelector('.v2-user-avatar')).toHaveClass('semi-avatar');
|
||||
expect(document.querySelector('.v2-user-status-tag')).toHaveClass('semi-tag');
|
||||
expect(document.querySelector('.v2-grant-history')).toHaveClass('semi-collapse');
|
||||
expect(document.querySelector('.v2-grant-history-title .semi-tag')).toHaveTextContent('1 条');
|
||||
|
||||
fireEvent.change(screen.getByRole('textbox', { name: '按车牌或 VIN 搜索' }), { target: { value: '粤A22222' } });
|
||||
await waitFor(() => expect(mocks.vehicleCoverage).toHaveBeenCalled());
|
||||
const candidates = await screen.findAllByRole('button', { name: /粤A22222.*VIN002.*选择/ });
|
||||
const candidates = await screen.findAllByRole('option', { name: /粤A22222.*VIN002.*选择/ });
|
||||
expect(candidates).toHaveLength(1);
|
||||
expect((mocks.vehicleCoverage.mock.calls[0][0] as URLSearchParams).get('keyword')).toBe('粤A22222');
|
||||
|
||||
fireEvent.click(candidates[0]);
|
||||
expect((await screen.findByRole('button', { name: '移除 粤A22222' })).closest('article')).toHaveTextContent('粤A22222VIN002');
|
||||
expect((await screen.findByRole('button', { name: '移除 粤A22222' })).closest('.v2-assigned-vehicle-card')).toHaveTextContent('粤A22222VIN002');
|
||||
});
|
||||
|
||||
test('pages and filters large vehicle grants without nesting a vehicle-list scrollbar', async () => {
|
||||
const vehicles = Array.from({ length: 12 }, (_, index) => ({
|
||||
vin: `VIN${String(index + 1).padStart(3, '0')}`,
|
||||
plate: `粤A${String(index + 1).padStart(5, '0')}`,
|
||||
validFrom: '2026-06-05T00:00:00+08:00',
|
||||
sourceSystem: 'manual',
|
||||
grantedBy: '平台管理员'
|
||||
}));
|
||||
mocks.adminUsers.mockResolvedValue([{
|
||||
id: 7,
|
||||
username: 'customer-east',
|
||||
displayName: '华东客户',
|
||||
userType: 'customer',
|
||||
status: 'enabled',
|
||||
customerRef: '',
|
||||
tenantRef: '',
|
||||
authProvider: 'local',
|
||||
menuKeys: ['monitor', 'vehicles', 'tracks', 'statistics'],
|
||||
vehicleVins: vehicles.map((vehicle) => vehicle.vin),
|
||||
vehicles,
|
||||
grantHistory: [],
|
||||
createdAt: '2026-07-16T00:00:00Z',
|
||||
updatedAt: '2026-07-16T00:00:00Z'
|
||||
}]);
|
||||
const client = new QueryClient({ defaultOptions: { queries: { retry: false } } });
|
||||
|
||||
const view = render(<QueryClientProvider client={client}><UsersPage /></QueryClientProvider>);
|
||||
fireEvent.click(await screen.findByRole('button', { name: /选择客户 华东客户/ }));
|
||||
|
||||
expect(screen.getAllByRole('button', { name: /^移除 / })).toHaveLength(10);
|
||||
expect(screen.getByText('第 1–10 条,共 12 辆')).toBeInTheDocument();
|
||||
expect(view.container.querySelector('.v2-assigned-pagination')).toBeInTheDocument();
|
||||
fireEvent.click(screen.getByRole('button', { name: '下一页' }));
|
||||
expect(await screen.findByRole('button', { name: '移除 粤A00012' })).toBeInTheDocument();
|
||||
expect(screen.getAllByRole('button', { name: /^移除 / })).toHaveLength(2);
|
||||
|
||||
fireEvent.change(screen.getByRole('textbox', { name: '筛选已授权车辆' }), { target: { value: '粤A00012' } });
|
||||
expect(screen.getAllByRole('button', { name: /^移除 / })).toHaveLength(1);
|
||||
expect(screen.getByText('1 条匹配结果')).toBeInTheDocument();
|
||||
expect(screen.getByText('第 1–1 条,共 1 辆')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('keeps the customer directory visible on mobile and opens details on demand', async () => {
|
||||
layout.mobile = true;
|
||||
mocks.adminUsers.mockResolvedValue([{
|
||||
id: 7, username: 'customer-east', displayName: '华东客户', userType: 'customer', status: 'enabled',
|
||||
customerRef: '', tenantRef: '', authProvider: 'local', menuKeys: ['monitor'], vehicleVins: [],
|
||||
vehicles: [], grantHistory: [], createdAt: '2026-07-16T00:00:00Z', updatedAt: '2026-07-16T00:00:00Z'
|
||||
}]);
|
||||
const client = new QueryClient({ defaultOptions: { queries: { retry: false } } });
|
||||
const view = render(<QueryClientProvider client={client}><UsersPage /></QueryClientProvider>);
|
||||
|
||||
expect(await screen.findByText('客户权限目录')).toBeInTheDocument();
|
||||
const customer = await screen.findByRole('button', { name: /选择客户 华东客户/ });
|
||||
expect(view.container.querySelector('.v2-user-mobile-picker')).not.toBeInTheDocument();
|
||||
expect(view.container.querySelector('.v2-customer-list.semi-list')).toBeInTheDocument();
|
||||
expect(screen.getByRole('textbox', { name: '搜索客户账号' })).toBeInTheDocument();
|
||||
expect(view.container.querySelector('.v2-user-editor')).not.toBeInTheDocument();
|
||||
fireEvent.click(customer);
|
||||
expect(await screen.findByRole('button', { name: '关闭账号详情' })).toBeInTheDocument();
|
||||
expect(customer).toHaveAttribute('aria-expanded', 'true');
|
||||
fireEvent.click(screen.getByRole('button', { name: '关闭账号详情' }));
|
||||
expect(view.container.querySelector('.v2-user-editor')).not.toBeInTheDocument();
|
||||
expect(customer).toHaveAttribute('aria-expanded', 'false');
|
||||
expect(screen.getAllByRole('button', { name: '新建客户账号' })).toHaveLength(1);
|
||||
});
|
||||
|
||||
test('uses a standard Semi empty state when no customer account exists', async () => {
|
||||
mocks.adminUsers.mockResolvedValue([]);
|
||||
const client = new QueryClient({ defaultOptions: { queries: { retry: false } } });
|
||||
const view = render(<QueryClientProvider client={client}><UsersPage /></QueryClientProvider>);
|
||||
|
||||
expect(await screen.findByText('还没有客户账号')).toBeInTheDocument();
|
||||
expect(view.container.querySelector('.v2-user-list-empty.semi-empty')).toBeInTheDocument();
|
||||
expect(view.container.querySelector('.v2-user-editor')).not.toBeInTheDocument();
|
||||
expect(screen.getAllByRole('button', { name: '创建第一个客户账号' })).toHaveLength(1);
|
||||
});
|
||||
|
||||
test('submits per-vehicle authorization interval instead of only a VIN list', async () => {
|
||||
@@ -63,8 +176,11 @@ test('submits per-vehicle authorization interval instead of only a VIN list', as
|
||||
mocks.updateCustomerUser.mockResolvedValue({ id: 7 });
|
||||
const client = new QueryClient({ defaultOptions: { queries: { retry: false } } });
|
||||
render(<QueryClientProvider client={client}><UsersPage /></QueryClientProvider>);
|
||||
fireEvent.click(await screen.findByRole('button', { name: /华东客户/ }));
|
||||
fireEvent.click(await screen.findByRole('button', { name: /选择客户 华东客户/ }));
|
||||
expect(document.querySelectorAll('input[type="datetime-local"]')).toHaveLength(0);
|
||||
fireEvent.click(screen.getByRole('button', { name: '调整 粤A11111 有效期' }));
|
||||
fireEvent.change(await screen.findByLabelText('粤A11111 启用时间'), { target: { value: '2026-06-05T00:00' } });
|
||||
fireEvent.click(screen.getByRole('button', { name: '完成' }));
|
||||
fireEvent.click(screen.getByRole('button', { name: '保存权限' }));
|
||||
await waitFor(() => expect(mocks.updateCustomerUser).toHaveBeenCalled());
|
||||
expect(mocks.updateCustomerUser.mock.calls[0][1]).toMatchObject({
|
||||
|
||||
Reference in New Issue
Block a user