refine Semi UI platform shell and states

This commit is contained in:
lingniu
2026-07-18 01:12:48 +08:00
parent 1cd92715a5
commit 5c0a61f7bd
9 changed files with 1298 additions and 54 deletions

View File

@@ -12,11 +12,15 @@ const auth = vi.hoisted(() => ({
session: { name: 'test-user', username: 'test-user', role: 'viewer' as const } as any,
logout: vi.fn()
}));
const apiMocks = vi.hoisted(() => ({
changePassword: vi.fn()
}));
vi.mock('../routing/routeModules', () => routing);
vi.mock('../auth/AuthGate', () => ({
usePlatformSession: () => ({ session: auth.session, logout: auth.logout })
}));
vi.mock('../../api/client', () => ({ api: { changePassword: apiMocks.changePassword } }));
import { AppShell } from './AppShell';
@@ -25,6 +29,7 @@ afterEach(() => {
vi.restoreAllMocks();
vi.clearAllMocks();
auth.session = { name: 'test-user', username: 'test-user', role: 'viewer' };
apiMocks.changePassword.mockReset();
});
test('renders only explicitly assigned customer menus', () => {
@@ -91,6 +96,8 @@ test('opens contextual help for the active module and closes it without navigati
fireEvent.click(help);
expect(screen.getByRole('dialog', { name: '全局监控' })).toHaveTextContent('筛选会自动生效');
expect(document.querySelector('.v2-help-overview.semi-card')).toBeInTheDocument();
expect(screen.getByText('建议操作')).toBeInTheDocument();
expect(help).toHaveAttribute('aria-expanded', 'true');
fireEvent.click(screen.getByLabelText('关闭帮助'));
expect(help).toHaveAttribute('aria-expanded', 'false');
@@ -108,6 +115,7 @@ test('uses one Semi account menu for identity, password and logout actions', asy
fireEvent.click(trigger);
expect(await screen.findByText('@test-user')).toBeInTheDocument();
expect(screen.getByText('当前登录账号')).toBeInTheDocument();
await waitFor(() => expect(screen.getByRole('button', { name: '账号菜单test-user' })).toHaveAttribute('aria-expanded', 'true'));
fireEvent.click(screen.getByText('修改登录密码'));
@@ -119,6 +127,35 @@ test('uses one Semi account menu for identity, password and logout actions', asy
expect(auth.logout).toHaveBeenCalledTimes(1);
});
test('validates password policy while typing and submits only a complete secure change', async () => {
apiMocks.changePassword.mockResolvedValue({ changed: true });
render(<MemoryRouter future={ROUTER_FUTURE} initialEntries={['/monitor']}>
<Routes><Route element={<AppShell />}><Route path="*" element={<span></span>} /></Route></Routes>
</MemoryRouter>);
fireEvent.click(screen.getByRole('button', { name: '账号菜单test-user' }));
fireEvent.click(await screen.findByText('修改登录密码'));
const submit = screen.getByRole('button', { name: '确认修改' });
expect(submit).toBeDisabled();
expect(document.querySelector('.v2-password-dialog > .semi-banner-warning')).toHaveTextContent('当前设备和其他设备上的旧会话都会失效');
fireEvent.change(screen.getByLabelText('当前密码'), { target: { value: 'CurrentPass!1' } });
fireEvent.change(screen.getByLabelText('新密码'), { target: { value: 'StrongPass!1' } });
expect(screen.getByLabelText('新密码安全规则').querySelectorAll('.is-valid')).toHaveLength(2);
fireEvent.change(screen.getByLabelText('确认新密码'), { target: { value: 'different' } });
expect(screen.getByRole('status')).toHaveTextContent('两次输入的新密码不一致');
expect(submit).toBeDisabled();
fireEvent.change(screen.getByLabelText('确认新密码'), { target: { value: 'StrongPass!1' } });
expect(screen.getByRole('status')).toHaveTextContent('两次输入一致');
expect(submit).toBeEnabled();
fireEvent.click(submit);
await waitFor(() => expect(apiMocks.changePassword).toHaveBeenCalledWith({ currentPassword: 'CurrentPass!1', newPassword: 'StrongPass!1' }));
expect(auth.logout).toHaveBeenCalledTimes(1);
});
test('uses the compact mobile navigation and opens secondary modules in a Semi SideSheet', () => {
vi.spyOn(window, 'matchMedia').mockReturnValue({
matches: true,