import { fireEvent, render, screen, waitFor } from '@testing-library/react' import { useSession } from 'next-auth/react' import SystemSettings from '@/app/admin/settings/page' const showToastMock = jest.fn() jest.mock('next-auth/react', () => ({ useSession: jest.fn(), })) jest.mock('next/navigation', () => ({ useRouter: () => ({ push: jest.fn(), refresh: jest.fn(), }), })) jest.mock('@/components/Header', () => ({ __esModule: true, default: () =>
, })) jest.mock('@/hooks/useToast', () => ({ useToast: () => ({ toast: null, showToast: showToastMock, }), })) jest.mock('@/components/ToastBubble', () => ({ __esModule: true, default: () => null, })) describe('系统设置页凭据脱敏', () => { beforeEach(() => { jest.clearAllMocks() ;(useSession as jest.Mock).mockReturnValue({ data: { user: { id: 'admin-1', role: 'ADMIN' }, }, status: 'authenticated', }) global.fetch = jest.fn().mockResolvedValue({ ok: true, json: async () => ({ headerBgColor: '#1f2937', siteName: '由壹点3D素材', headerLogoUrl: 'https://s3.jiansun.vip/gongxiangpan/brand/header.png', headerLogoPreviewUrl: '/api/proxy-media?url=header-preview', loginLogoUrl: 'https://s3.jiansun.vip/gongxiangpan/brand/login.png', loginLogoPreviewUrl: '/api/proxy-media?url=login-preview', s3Endpoint: 'https://storage.example.com', s3Bucket: 'materials', s3Region: 'us-east-1', s3PublicUrl: 'https://cdn.example.com/materials', s3CredentialsConfigured: true, // 即使旧服务误返回这些字段,页面也不得回填或展示。 s3AccessKey: 'should-never-render-access-key', s3SecretKey: 'should-never-render-secret-key', maintenanceEnabled: false, maintenanceMessage: null, maintenanceStartTime: null, maintenanceEndTime: null, }), }) as jest.Mock }) test('只显示 configured 状态,凭据输入框始终为空', async () => { render() await waitFor(() => { expect(screen.getByText('已配置')).toBeInTheDocument() }) expect(screen.getByLabelText('新 Access Key')).toHaveValue('') expect(screen.getByLabelText('新 Secret Key')).toHaveValue('') expect(screen.queryByDisplayValue('should-never-render-access-key')).not.toBeInTheDocument() expect(screen.queryByDisplayValue('should-never-render-secret-key')).not.toBeInTheDocument() expect(screen.queryByText('should-never-render-access-key')).not.toBeInTheDocument() expect(screen.queryByText('should-never-render-secret-key')).not.toBeInTheDocument() }) test('未填写新凭据时,保存请求不会覆盖现有凭据', async () => { render() await waitFor(() => { expect(screen.getByText('已配置')).toBeInTheDocument() }) fireEvent.click(screen.getByRole('button', { name: '保存设置' })) await waitFor(() => { expect(global.fetch).toHaveBeenCalledTimes(2) }) const [, requestInit] = (global.fetch as jest.Mock).mock.calls[1] const payload = JSON.parse(requestInit.body) expect(payload).not.toHaveProperty('s3AccessKey') expect(payload).not.toHaveProperty('s3SecretKey') expect(payload.headerLogoUrl).toBe('https://s3.jiansun.vip/gongxiangpan/brand/header.png') expect(payload.loginLogoUrl).toBe('https://s3.jiansun.vip/gongxiangpan/brand/login.png') expect(JSON.stringify(payload)).not.toContain('/api/proxy-media') }) test('恢复默认使用镜像内置品牌图,不再写入旧存储域名', async () => { render() await waitFor(() => { expect(screen.getByText('已配置')).toBeInTheDocument() }) const resetButtons = screen.getAllByRole('button', { name: '恢复默认' }) fireEvent.click(resetButtons[0]) fireEvent.click(resetButtons[1]) fireEvent.click(screen.getByRole('button', { name: '保存设置' })) await waitFor(() => { expect(global.fetch).toHaveBeenCalledTimes(2) }) const [, requestInit] = (global.fetch as jest.Mock).mock.calls[1] const payload = JSON.parse(requestInit.body) expect(payload.headerLogoUrl).toBe('/brand/header-logo.png') expect(payload.loginLogoUrl).toBe('/brand/login-logo.png') expect(JSON.stringify(payload)).not.toContain('s3.1080p.hk') }) test('文件选择器和前端校验只接受服务端支持的三种 Logo 格式', async () => { const { container } = render() await waitFor(() => { expect(screen.getByText('已配置')).toBeInTheDocument() }) const inputs = Array.from(container.querySelectorAll('input[type="file"]')) expect(inputs).toHaveLength(2) expect(inputs.every((input) => input.accept === 'image/png,image/jpeg,image/webp')).toBe(true) fireEvent.change(inputs[0], { target: { files: [new File([''], 'logo.svg', { type: 'image/svg+xml' })] }, }) expect(showToastMock).toHaveBeenCalledWith( 'LOGO仅支持 PNG、JPEG 或 WebP 格式', { variant: 'error' }, ) expect(global.fetch).toHaveBeenCalledTimes(1) }) test.each([ { user: { id: 'system-admin-1', role: 'SYSTEM_ADMIN' }, label: 'SYSTEM_ADMIN' }, { user: { id: 'super-admin-1', role: 'USER', isSuperAdmin: true }, label: '超级管理员' }, ])('$label 权限账号可以正常看到设置页面', async ({ user }) => { ;(useSession as jest.Mock).mockReturnValue({ data: { user }, status: 'authenticated', }) render() expect(await screen.findByRole('heading', { name: '系统设置' })).toBeInTheDocument() }) })