181 lines
6.2 KiB
TypeScript
181 lines
6.2 KiB
TypeScript
import { render, screen, waitFor } from '@testing-library/react'
|
|
import { describe, expect, it, vi, beforeEach } from 'vitest'
|
|
|
|
import { SettingsPage } from './SettingsPage'
|
|
import type { SystemSettings } from '@/services/settings'
|
|
|
|
vi.mock('@ant-design/icons', () => ({
|
|
SafetyOutlined: () => <span>safety</span>,
|
|
SettingOutlined: () => <span>setting</span>,
|
|
EnvironmentOutlined: () => <span>environment</span>,
|
|
}))
|
|
|
|
vi.mock('@/services/settings', () => ({
|
|
getSettings: vi.fn(),
|
|
}))
|
|
|
|
const mockSettings: SystemSettings = {
|
|
system: {
|
|
name: '用户管理系统',
|
|
version: '1.0.0',
|
|
environment: 'Production',
|
|
description: '基于 Go + React 的现代化用户管理系统',
|
|
},
|
|
security: {
|
|
password_min_length: 8,
|
|
password_require_uppercase: true,
|
|
password_require_lowercase: true,
|
|
password_require_numbers: true,
|
|
password_require_symbols: true,
|
|
password_history: 5,
|
|
totp_enabled: true,
|
|
login_fail_lock: true,
|
|
login_fail_threshold: 5,
|
|
login_fail_duration: 30,
|
|
session_timeout: 86400,
|
|
device_trust_duration: 2592000,
|
|
},
|
|
features: {
|
|
email_verification: true,
|
|
phone_verification: false,
|
|
oauth_providers: ['GitHub', 'Google'],
|
|
sso_enabled: false,
|
|
operation_log_enabled: true,
|
|
login_log_enabled: true,
|
|
data_export_enabled: true,
|
|
data_import_enabled: true,
|
|
},
|
|
}
|
|
|
|
describe('SettingsPage', () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks()
|
|
})
|
|
|
|
it('renders page header with title and description', async () => {
|
|
const { getSettings } = await import('@/services/settings')
|
|
vi.mocked(getSettings).mockResolvedValue(mockSettings)
|
|
|
|
render(<SettingsPage />)
|
|
|
|
await waitFor(() => {
|
|
expect(screen.getByText('安全设置')).toBeInTheDocument()
|
|
})
|
|
expect(screen.getByText('系统设置')).toBeInTheDocument()
|
|
expect(screen.getByText('查看当前系统配置和功能开关状态')).toBeInTheDocument()
|
|
})
|
|
|
|
it('renders security settings section', async () => {
|
|
const { getSettings } = await import('@/services/settings')
|
|
vi.mocked(getSettings).mockResolvedValue(mockSettings)
|
|
|
|
render(<SettingsPage />)
|
|
|
|
await waitFor(() => {
|
|
expect(screen.getByText('安全设置')).toBeInTheDocument()
|
|
})
|
|
expect(screen.getByText('密码最小长度')).toBeInTheDocument()
|
|
expect(screen.getByText('密码必须包含大写字母')).toBeInTheDocument()
|
|
expect(screen.getByText('密码必须包含小写字母')).toBeInTheDocument()
|
|
expect(screen.getByText('密码必须包含数字')).toBeInTheDocument()
|
|
expect(screen.getByText('密码必须包含特殊字符')).toBeInTheDocument()
|
|
})
|
|
|
|
it('renders feature toggles section', async () => {
|
|
const { getSettings } = await import('@/services/settings')
|
|
vi.mocked(getSettings).mockResolvedValue(mockSettings)
|
|
|
|
render(<SettingsPage />)
|
|
|
|
await waitFor(() => {
|
|
expect(screen.getByText('功能开关')).toBeInTheDocument()
|
|
})
|
|
expect(screen.getByText('邮箱验证')).toBeInTheDocument()
|
|
expect(screen.getByText('手机验证')).toBeInTheDocument()
|
|
expect(screen.getByText('OAuth 提供商')).toBeInTheDocument()
|
|
expect(screen.getByText('GitHub, Google')).toBeInTheDocument()
|
|
expect(screen.getByText('SSO 单点登录')).toBeInTheDocument()
|
|
expect(screen.getByText('操作日志')).toBeInTheDocument()
|
|
expect(screen.getByText('登录日志')).toBeInTheDocument()
|
|
expect(screen.getByText('数据导出')).toBeInTheDocument()
|
|
expect(screen.getByText('数据导入')).toBeInTheDocument()
|
|
})
|
|
|
|
it('renders system information section', async () => {
|
|
const { getSettings } = await import('@/services/settings')
|
|
vi.mocked(getSettings).mockResolvedValue(mockSettings)
|
|
|
|
render(<SettingsPage />)
|
|
|
|
await waitFor(() => {
|
|
expect(screen.getByText('系统信息')).toBeInTheDocument()
|
|
})
|
|
expect(screen.getByText('系统名称')).toBeInTheDocument()
|
|
expect(screen.getByText('用户管理系统')).toBeInTheDocument()
|
|
expect(screen.getByText('版本号')).toBeInTheDocument()
|
|
expect(screen.getByText('1.0.0')).toBeInTheDocument()
|
|
expect(screen.getByText('运行环境')).toBeInTheDocument()
|
|
expect(screen.getByText('Production')).toBeInTheDocument()
|
|
expect(screen.getByText('系统描述')).toBeInTheDocument()
|
|
expect(screen.getByText('基于 Go + React 的现代化用户管理系统')).toBeInTheDocument()
|
|
})
|
|
|
|
it('renders password history setting', async () => {
|
|
const { getSettings } = await import('@/services/settings')
|
|
vi.mocked(getSettings).mockResolvedValue(mockSettings)
|
|
|
|
render(<SettingsPage />)
|
|
|
|
await waitFor(() => {
|
|
expect(screen.getByText('密码历史记录')).toBeInTheDocument()
|
|
})
|
|
expect(screen.getByText(/最近 5 次$/)).toBeInTheDocument()
|
|
})
|
|
|
|
it('renders TOTP setting', async () => {
|
|
const { getSettings } = await import('@/services/settings')
|
|
vi.mocked(getSettings).mockResolvedValue(mockSettings)
|
|
|
|
render(<SettingsPage />)
|
|
|
|
await waitFor(() => {
|
|
expect(screen.getByText('TOTP 两步验证')).toBeInTheDocument()
|
|
})
|
|
const totpEnabled = screen.getAllByText('已启用')
|
|
expect(totpEnabled.length).toBeGreaterThan(0)
|
|
})
|
|
|
|
it('shows readonly notice in header actions', async () => {
|
|
const { getSettings } = await import('@/services/settings')
|
|
vi.mocked(getSettings).mockResolvedValue(mockSettings)
|
|
|
|
render(<SettingsPage />)
|
|
|
|
await waitFor(() => {
|
|
expect(screen.getByText('配置更新请联系管理员')).toBeInTheDocument()
|
|
})
|
|
})
|
|
|
|
it('shows loading state while fetching settings', async () => {
|
|
const { getSettings } = await import('@/services/settings')
|
|
// Don't resolve the promise - keep it pending to show loading state
|
|
vi.mocked(getSettings).mockImplementation(() => new Promise(() => {}))
|
|
|
|
render(<SettingsPage />)
|
|
|
|
// Should show loading spinner
|
|
expect(document.querySelector('.ant-spin')).toBeInTheDocument()
|
|
})
|
|
|
|
it('shows error state when API fails', async () => {
|
|
const { getSettings } = await import('@/services/settings')
|
|
vi.mocked(getSettings).mockRejectedValue(new Error('网络错误'))
|
|
|
|
render(<SettingsPage />)
|
|
|
|
await waitFor(() => {
|
|
expect(screen.getByText('网络错误')).toBeInTheDocument()
|
|
})
|
|
})
|
|
})
|