test: realign verification baseline and supporting tests

This commit is contained in:
Your Name
2026-05-28 15:19:34 +08:00
parent 6be90ddff8
commit 260046a581
31 changed files with 454 additions and 1936 deletions

View File

@@ -9,7 +9,7 @@
import { Spin, Button, Result, Empty, type ButtonProps } from 'antd'
import { ReloadOutlined, PlusOutlined } from '@ant-design/icons'
import type { ReactNode } from 'react'
import { Children, type ReactNode } from 'react'
import styles from './PageState.module.css'
// ==================== PageLoading ====================
@@ -94,19 +94,14 @@ export function PageError({
status="error"
title={title}
subTitle={description}
extra={[
onRetry && (
<Button
key="retry"
type="primary"
icon={<ReloadOutlined />}
onClick={onRetry}
>
extra={Children.toArray([
onRetry ? (
<Button type="primary" icon={<ReloadOutlined />} onClick={onRetry}>
{retryText}
</Button>
),
) : null,
extra,
].filter(Boolean)}
])}
/>
</div>
)

View File

@@ -51,7 +51,7 @@ describe('RequireAuth', () => {
it('shows a loading indicator while auth state is being restored', () => {
const { container } = renderWithAuth(
{ isLoading: true },
<MemoryRouter initialEntries={['/users']}>
<MemoryRouter future={{ v7_startTransition: true, v7_relativeSplatPath: true }} initialEntries={['/users']}>
<Routes>
<Route
path="/users"
@@ -72,7 +72,7 @@ describe('RequireAuth', () => {
it('redirects unauthenticated users to login and preserves the original route', async () => {
renderWithAuth(
{ isAuthenticated: false, isLoading: false },
<MemoryRouter initialEntries={['/users']}>
<MemoryRouter future={{ v7_startTransition: true, v7_relativeSplatPath: true }} initialEntries={['/users']}>
<Routes>
<Route
path="/users"
@@ -106,7 +106,7 @@ describe('RequireAuth', () => {
status: 1,
},
},
<MemoryRouter initialEntries={['/users']}>
<MemoryRouter future={{ v7_startTransition: true, v7_relativeSplatPath: true }} initialEntries={['/users']}>
<Routes>
<Route
path="/users"
@@ -128,7 +128,7 @@ describe('RequireAdmin', () => {
it('waits silently while auth state is still loading', () => {
const { container } = renderWithAuth(
{ isLoading: true, isAdmin: false },
<MemoryRouter initialEntries={['/dashboard']}>
<MemoryRouter future={{ v7_startTransition: true, v7_relativeSplatPath: true }} initialEntries={['/dashboard']}>
<Routes>
<Route
path="/dashboard"
@@ -148,7 +148,7 @@ describe('RequireAdmin', () => {
it('redirects non-admin users to profile', async () => {
renderWithAuth(
{ isLoading: false, isAdmin: false, isAuthenticated: true },
<MemoryRouter initialEntries={['/dashboard']}>
<MemoryRouter future={{ v7_startTransition: true, v7_relativeSplatPath: true }} initialEntries={['/dashboard']}>
<Routes>
<Route
path="/dashboard"
@@ -169,7 +169,7 @@ describe('RequireAdmin', () => {
it('renders admin-only content for admins', () => {
renderWithAuth(
{ isLoading: false, isAdmin: true, isAuthenticated: true },
<MemoryRouter initialEntries={['/dashboard']}>
<MemoryRouter future={{ v7_startTransition: true, v7_relativeSplatPath: true }} initialEntries={['/dashboard']}>
<Routes>
<Route
path="/dashboard"

View File

@@ -321,7 +321,7 @@ function renderAdminLayout(
}
return render(
<MemoryRouter initialEntries={[initialEntry]}>
<MemoryRouter future={{ v7_startTransition: true, v7_relativeSplatPath: true }} initialEntries={[initialEntry]}>
<AuthContext.Provider value={value}>
<Routes>
<Route path="/" element={<AdminLayout>{layoutChildren}</AdminLayout>}>

View File

@@ -7,7 +7,7 @@ import { useBreadcrumbs } from './useBreadcrumbs'
function createWrapper(pathname: string) {
return function Wrapper({ children }: { children: ReactNode }) {
return <MemoryRouter initialEntries={[pathname]}>{children}</MemoryRouter>
return <MemoryRouter future={{ v7_startTransition: true, v7_relativeSplatPath: true }} initialEntries={[pathname]}>{children}</MemoryRouter>
}
}

View File

@@ -416,6 +416,7 @@ describe('DevicesPage', () => {
it('renders page header with title and description', async () => {
render(<DevicesPage />)
await screen.findByText('Device 1')
const header = screen.getByTestId('page-header')
expect(within(header).getByText('设备管理')).toBeInTheDocument()
expect(within(header).getByText('管理系统所有设备,支持查看、信任状态管理和删除')).toBeInTheDocument()

View File

@@ -46,6 +46,7 @@ vi.mock('antd', async () => {
htmlType,
type: buttonType,
icon,
danger,
...props
}: {
children?: ReactNode
@@ -55,6 +56,7 @@ vi.mock('antd', async () => {
}) => {
void buttonType
void icon
void danger
return (
<button type={htmlType ?? 'button'} onClick={onClick} {...props}>

View File

@@ -58,6 +58,9 @@ describe('SettingsPage', () => {
render(<SettingsPage />)
await waitFor(() => {
expect(screen.getByText('安全设置')).toBeInTheDocument()
})
expect(screen.getByText('系统设置')).toBeInTheDocument()
expect(screen.getByText('查看当前系统配置和功能开关状态')).toBeInTheDocument()
})

View File

@@ -18,7 +18,7 @@ vi.mock('@/services/auth', () => ({
function renderActivateAccountPage(initialEntry: string) {
return render(
<MemoryRouter initialEntries={[initialEntry]}>
<MemoryRouter future={{ v7_startTransition: true, v7_relativeSplatPath: true }} initialEntries={[initialEntry]}>
<Routes>
<Route path="/activate-account" element={<ActivateAccountPage />} />
</Routes>

View File

@@ -17,7 +17,7 @@ vi.mock('@/services/auth', () => ({
function renderForgotPasswordPage() {
return render(
<MemoryRouter initialEntries={['/forgot-password']}>
<MemoryRouter future={{ v7_startTransition: true, v7_relativeSplatPath: true }} initialEntries={['/forgot-password']}>
<Routes>
<Route path="/forgot-password" element={<ForgotPasswordPage />} />
</Routes>

View File

@@ -100,7 +100,7 @@ function renderLoginPage(
} = '/login',
) {
return render(
<MemoryRouter initialEntries={[initialEntry]}>
<MemoryRouter future={{ v7_startTransition: true, v7_relativeSplatPath: true }} initialEntries={[initialEntry]}>
<AuthContext.Provider value={authContextValue}>
<LoginPage />
</AuthContext.Provider>

View File

@@ -25,7 +25,7 @@ const authContextValue: AuthContextValue = {
function renderOAuthCallbackPage(entry: string) {
return render(
<MemoryRouter initialEntries={[entry]}>
<MemoryRouter future={{ v7_startTransition: true, v7_relativeSplatPath: true }} initialEntries={[entry]}>
<AuthContext.Provider value={authContextValue}>
<OAuthCallbackPage />
</AuthContext.Provider>

View File

@@ -16,7 +16,7 @@ vi.mock('@/services/auth', () => ({
function renderResetPasswordPage(initialEntry: string) {
return render(
<MemoryRouter initialEntries={[initialEntry]}>
<MemoryRouter future={{ v7_startTransition: true, v7_relativeSplatPath: true }} initialEntries={[initialEntry]}>
<Routes>
<Route path="/reset-password" element={<ResetPasswordPage />} />
</Routes>

View File

@@ -29,7 +29,7 @@ describe('profile service', () => {
const { getCurrentProfile } = await import('./profile')
const result = await getCurrentProfile(1)
expect(getMock).toHaveBeenCalledWith('/users/1')
expect(getMock).toHaveBeenCalledWith('/auth/userinfo')
expect(result).toEqual({
user: { id: 1, username: 'admin', nickname: 'Admin' },
roles: [{ id: 2, name: '管理员' }],

View File

@@ -32,7 +32,7 @@ export interface TOTPSetupResponse {
export async function getCurrentProfile(userId: number): Promise<CurrentUserProfile> {
const [user, roles] = await Promise.all([
get<User>(`/users/${userId}`),
get<User>('/auth/userinfo'),
getUserRoles(userId),
])

View File

@@ -221,7 +221,7 @@ describe('additional service adapters', () => {
user: { id: 1, username: 'admin' },
roles: [{ id: 2, name: '管理员' }],
})
expect(getMock).toHaveBeenNthCalledWith(1, '/users/1')
expect(getMock).toHaveBeenNthCalledWith(1, '/auth/userinfo')
expect(getMock).toHaveBeenNthCalledWith(2, '/users/1/roles')
await updateProfile(1, { nickname: 'Admin User' })

View File

@@ -25,6 +25,7 @@ export interface TOTPVerifyRequest {
user_id: number
code: string
device_id?: string
temp_token: string
}
export interface OAuthProviderInfo {
@@ -90,14 +91,12 @@ export interface RegisterRequest {
export interface BootstrapAdminRequest {
username: string
password: string
email?: string
email: string
nickname?: string
bootstrap_secret: string
}
export interface RegisterResponse {
user: SessionUser
message: string
}
export type RegisterResponse = SessionUser
export interface ActionMessageResponse {
message: string