AppLayout.topbar.test.tsx
4.86 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
// REQ-USR-004: 顶栏结构 + 当前用户 + 退出登录(BR3/BR9)
import { describe, it, expect, vi } from 'vitest';
import { screen, within } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { useNavigate, useLocation, Routes, Route } from 'react-router-dom';
import { App as AntdApp } from 'antd';
import { renderShell } from './renderShell';
import TopBar from '../../src/layouts/AppLayout/TopBar';
import { useAppDispatch } from '../../src/store/hooks';
import { clearCredentials } from '../../src/store/slices/authSlice';
import { LOGOUT_SUCCESS_TEXT } from '../../src/layouts/AppLayout/shellMessages';
import { HOME_TAB, BIZ_TABS, type TabItem } from '../../src/layouts/AppLayout/useTabStack';
import type { AuthUser } from '../../src/api/types';
const ADMIN: AuthUser = { id: 1, sUserName: '朱子纯', sUserType: '超级管理员', sLanguage: '中文' };
// 顶栏测试宿主:以真实 onLogout(dispatch + message + navigate)驱动,模拟 AppLayout 提供的回调
function TopBarHost({
user,
tabs,
activeKey = 'home',
navOverlayOpen = false,
onToggleNav = vi.fn(),
onSelectTab = vi.fn(),
onCloseTab = vi.fn(),
onLogoHome = vi.fn(),
}: {
user: AuthUser | null;
tabs: TabItem[];
activeKey?: string;
navOverlayOpen?: boolean;
onToggleNav?: () => void;
onSelectTab?: (k: string) => void;
onCloseTab?: (k: string) => void;
onLogoHome?: () => void;
}) {
const dispatch = useAppDispatch();
const navigate = useNavigate();
const { message } = AntdApp.useApp();
const handleLogout = () => {
dispatch(clearCredentials());
message.success(LOGOUT_SUCCESS_TEXT);
navigate('/login', { replace: true });
};
return (
<TopBar
user={user}
tabs={tabs}
activeKey={activeKey}
navOverlayOpen={navOverlayOpen}
onToggleNav={onToggleNav}
onSelectTab={onSelectTab}
onCloseTab={onCloseTab}
onLogout={handleLogout}
onLogoHome={onLogoHome}
/>
);
}
function LoginProbe() {
const loc = useLocation();
return <div data-testid="login-probe">{loc.pathname}</div>;
}
describe('TopBar', () => {
it('renders brand logo / 全部导航 button / 主页 tab', () => {
renderShell(<TopBarHost user={ADMIN} tabs={[HOME_TAB]} />, {
preloadedAuth: { token: 't', user: ADMIN },
});
expect(screen.getByRole('button', { name: '全部导航' })).toBeInTheDocument();
expect(screen.getByRole('button', { name: '品牌Logo 回到主页' })).toBeInTheDocument();
const homeTab = screen.getByTestId('tab-home');
expect(homeTab).toHaveTextContent('主页');
// 主页 tab 无关闭按钮
expect(within(homeTab).queryByText('✕')).not.toBeInTheDocument();
});
it('renders current user as sUserName(sUserType)', () => {
renderShell(<TopBarHost user={ADMIN} tabs={[HOME_TAB]} />, {
preloadedAuth: { token: 't', user: ADMIN },
});
expect(screen.getByText('朱子纯(超级管理员)')).toBeInTheDocument();
});
it('user fallback when user is null', () => {
renderShell(<TopBarHost user={null} tabs={[HOME_TAB]} />, {
preloadedAuth: { token: 't', user: null },
});
expect(screen.getByText('未登录用户')).toBeInTheDocument();
});
it('logout menu dispatches clearCredentials, shows success, navigates /login', async () => {
localStorage.setItem('xly_erp_token', 't');
const { getState } = renderShell(
<Routes>
<Route path="/" element={<TopBarHost user={ADMIN} tabs={[HOME_TAB]} />} />
<Route path="/login" element={<LoginProbe />} />
</Routes>,
{ initialEntries: ['/'], preloadedAuth: { token: 't', user: ADMIN } },
);
// 展开当前用户下拉
await userEvent.click(screen.getByText('朱子纯(超级管理员)'));
const logout = await screen.findByText('退出登录');
await userEvent.click(logout);
expect(getState().auth.token).toBeNull();
expect(localStorage.getItem('xly_erp_token')).toBeNull();
expect(await screen.findByText(LOGOUT_SUCCESS_TEXT)).toBeInTheDocument();
expect(screen.getByTestId('login-probe').textContent).toBe('/login');
});
it('nav toggle button highlights when navOverlayOpen', () => {
renderShell(<TopBarHost user={ADMIN} tabs={[HOME_TAB]} navOverlayOpen />, {
preloadedAuth: { token: 't', user: ADMIN },
});
const navBtn = screen.getByRole('button', { name: '全部导航' });
expect(navBtn.getAttribute('aria-pressed')).toBe('true');
});
it('clicking business tab close calls onCloseTab', async () => {
const onCloseTab = vi.fn();
renderShell(
<TopBarHost
user={ADMIN}
tabs={[HOME_TAB, BIZ_TABS.userlist]}
activeKey="userlist"
onCloseTab={onCloseTab}
/>,
{ preloadedAuth: { token: 't', user: ADMIN } },
);
await userEvent.click(screen.getByTestId('tab-close-userlist'));
expect(onCloseTab).toHaveBeenCalledWith('userlist');
});
});