/** * 个人中心页面组件测试 */ import React from 'react' import { render, screen, fireEvent, waitFor } from '@testing-library/react' import '@testing-library/jest-dom' import { QueryClient, QueryClientProvider } from '@tanstack/react-query' import ProfilePage from '../../src/pages/profile/index' // 导入 Taro mock 函数 import taroMock from '../../tests/__mocks__/taroMock' // Mock TabBarLayout 组件 jest.mock('@/layouts/tab-bar-layout', () => ({ TabBarLayout: jest.fn(({ children, activeKey, className }) => (
{children}
)) })) // Mock Navbar 组件 jest.mock('@/components/ui/navbar', () => ({ Navbar: jest.fn(({ title, rightIcon, onClickRight, leftIcon, backgroundColor, textColor, border }) => (

{title}

)) })) // Mock AvatarUpload 组件 jest.mock('@/components/ui/avatar-upload', () => ({ AvatarUpload: jest.fn(({ currentAvatar, onUploadSuccess, onUploadError, size, editable, className }) => (
)) })) // Mock Button 组件 jest.mock('@/components/ui/button', () => ({ Button: jest.fn(({ children, variant, size, onClick, className }) => ( )) })) // Mock FAQDialog 组件 jest.mock('@/components/FAQDialog', () => ({ FAQDialog: jest.fn(({ open, onOpenChange }) => (
常见问题内容
)) })) // Mock useAuth hook const mockUser = { id: 1, username: '测试用户', avatarFile: { fullUrl: 'https://example.com/avatar.jpg' } } const mockLogout = jest.fn() const mockUpdateUser = jest.fn() jest.mock('@/utils/auth', () => ({ useAuth: jest.fn(() => ({ user: mockUser, logout: mockLogout, isLoading: false, updateUser: mockUpdateUser })) })) // Mock React Query hooks const mockUseQuery = jest.fn() const mockUseMutation = jest.fn() jest.mock('@tanstack/react-query', () => { const actual = jest.requireActual('@tanstack/react-query') return { ...actual, useQuery: (options: any) => mockUseQuery(options), useMutation: (options: any) => mockUseMutation(options) } }) // 创建测试用的 QueryClient const createTestQueryClient = () => new QueryClient({ defaultOptions: { queries: { retry: false, }, }, }) // 包装组件 const Wrapper = ({ children }: { children: React.ReactNode }) => { const queryClient = createTestQueryClient() return ( {children} ) } describe('个人中心页面测试', () => { beforeEach(() => { jest.clearAllMocks() // 设置环境变量 process.env.TARO_APP_WX_CORP_ID = 'wwc6d7911e2d23b7fb' process.env.TARO_APP_WX_KEFU_URL = 'https://work.weixin.qq.com/kfid/kfc5f4d729bc3c893d7' // 初始化 React Query mock mockUseQuery.mockImplementation(() => ({ data: null, isLoading: false })) mockUseMutation.mockImplementation((options) => ({ mutateAsync: options.mutationFn, isPending: false })) // 重置所有 mock 调用记录 taroMock.showToast.mockClear() taroMock.openCustomerServiceChat.mockClear() taroMock.navigateTo.mockClear() taroMock.showLoading.mockClear() taroMock.hideLoading.mockClear() taroMock.showModal.mockClear() taroMock.reLaunch.mockClear() }) test('应该正确渲染个人中心页面', () => { render( ) // 检查页面标题 expect(screen.getByText('个人中心')).toBeInTheDocument() // 检查用户信息 expect(screen.getByText('普通用户')).toBeInTheDocument() expect(screen.getByText('ID: 1')).toBeInTheDocument() // 检查功能菜单 expect(screen.getByText('我的服务')).toBeInTheDocument() expect(screen.getByText('编辑资料')).toBeInTheDocument() expect(screen.getByText('乘车人管理')).toBeInTheDocument() expect(screen.getByText('设置')).toBeInTheDocument() expect(screen.getByText('隐私政策')).toBeInTheDocument() expect(screen.getByText('帮助与反馈')).toBeInTheDocument() // 检查客服与帮助区域 expect(screen.getByText('客服与帮助')).toBeInTheDocument() expect(screen.getByText('联系客服')).toBeInTheDocument() expect(screen.getByText('7x24小时在线客服')).toBeInTheDocument() expect(screen.getByText('常见问题')).toBeInTheDocument() expect(screen.getByText('意见反馈')).toBeInTheDocument() // 检查版本信息 expect(screen.getByText('去看出行 v1.0.0')).toBeInTheDocument() }) test('应该处理联系客服功能 - 成功场景', async () => { taroMock.openCustomerServiceChat.mockImplementation((options) => { options.success() }) render( ) // 点击联系客服按钮 const customerServiceButton = screen.getByTestId('customer-service-button') fireEvent.click(customerServiceButton) // 检查微信客服API被正确调用 await waitFor(() => { expect(taroMock.openCustomerServiceChat).toHaveBeenCalledWith({ extInfo: { url: 'https://work.weixin.qq.com/kfid/kfc5f4d729bc3c893d7' }, corpId: 'wwc6d7911e2d23b7fb', success: expect.any(Function), fail: expect.any(Function) }) }) }) test('应该处理联系客服功能 - 失败场景', async () => { taroMock.openCustomerServiceChat.mockImplementation((options) => { options.fail({ errMsg: '客服功能不可用' }) }) render( ) // 点击联系客服按钮 const customerServiceButton = screen.getByTestId('customer-service-button') fireEvent.click(customerServiceButton) // 检查错误提示显示 await waitFor(() => { expect(taroMock.showToast).toHaveBeenCalledWith({ title: '客服功能暂不可用,请稍后重试', icon: 'none' }) }) }) test('应该处理联系客服功能 - 异常场景', async () => { taroMock.openCustomerServiceChat.mockImplementation(() => { throw new Error('API调用异常') }) render( ) // 点击联系客服按钮 const customerServiceButton = screen.getByTestId('customer-service-button') fireEvent.click(customerServiceButton) // 检查异常处理 await waitFor(() => { expect(taroMock.showToast).toHaveBeenCalledWith({ title: '客服功能异常,请稍后重试', icon: 'none' }) }) }) test('应该处理其他功能按钮点击', () => { render( ) // 点击编辑资料按钮 const editProfileButton = screen.getByTestId('edit-profile-button') fireEvent.click(editProfileButton) expect(taroMock.showToast).toHaveBeenCalledWith({ title: '功能开发中...', icon: 'none' }) // 点击乘车人管理按钮 const passengersButton = screen.getByTestId('passengers-button') fireEvent.click(passengersButton) expect(taroMock.navigateTo).toHaveBeenCalledWith({ url: '/pages/passengers/passengers' }) // 点击设置按钮 const settingsButton = screen.getByTestId('settings-button') fireEvent.click(settingsButton) expect(taroMock.showToast).toHaveBeenCalledWith({ title: '功能开发中...', icon: 'none' }) // 点击常见问题按钮 - 现在应该打开弹窗而不是显示Toast const faqButton = screen.getByTestId('faq-button') fireEvent.click(faqButton) // 检查弹窗状态变为打开 const faqDialog = screen.getByTestId('faq-dialog') expect(faqDialog).toHaveAttribute('data-open', 'true') // 点击意见反馈按钮 const feedbackButton = screen.getByTestId('feedback-button') fireEvent.click(feedbackButton) expect(taroMock.showToast).toHaveBeenCalledWith({ title: '意见反馈功能开发中...', icon: 'none' }) }) test('应该处理头像上传功能', async () => { render( ) // 点击头像上传成功按钮 const uploadButton = screen.getByTestId('avatar-upload-button') fireEvent.click(uploadButton) // 检查上传成功处理 await waitFor(() => { expect(taroMock.showLoading).toHaveBeenCalledWith({ title: '更新头像...' }) expect(taroMock.hideLoading).toHaveBeenCalled() expect(taroMock.showToast).toHaveBeenCalledWith({ title: '头像更新成功', icon: 'success' }) expect(mockUpdateUser).toHaveBeenCalledWith({ ...mockUser, avatarFileId: 'test-file-id' }) }) }) test('应该处理头像上传失败', async () => { render( ) // 点击头像上传失败按钮 const uploadErrorButton = screen.getByTestId('avatar-upload-error-button') fireEvent.click(uploadErrorButton) // 检查上传失败处理 await waitFor(() => { expect(taroMock.showToast).toHaveBeenCalledWith({ title: '上传失败,请重试', icon: 'none' }) }) }) // 退出登录功能暂时被注释掉,跳过相关测试 test.skip('应该处理退出登录', async () => { taroMock.showModal.mockImplementation((options) => { options.success({ confirm: true }) }) render( ) // 点击退出登录按钮 const logoutButton = screen.getByText('退出登录') fireEvent.click(logoutButton) // 检查退出登录流程 await waitFor(() => { expect(taroMock.showModal).toHaveBeenCalledWith({ title: '退出登录', content: '确定要退出登录吗?', success: expect.any(Function) }) expect(taroMock.showLoading).toHaveBeenCalledWith({ title: '退出中...' }) expect(mockLogout).toHaveBeenCalled() expect(taroMock.hideLoading).toHaveBeenCalled() expect(taroMock.showToast).toHaveBeenCalledWith({ title: '已退出登录', icon: 'success', duration: 1500 }) }) }) test.skip('应该处理退出登录取消', async () => { taroMock.showModal.mockImplementation((options) => { options.success({ confirm: false }) }) render( ) // 点击退出登录按钮 const logoutButton = screen.getByText('退出登录') fireEvent.click(logoutButton) // 检查取消退出登录 await waitFor(() => { expect(taroMock.showModal).toHaveBeenCalled() expect(mockLogout).not.toHaveBeenCalled() }) }) test('应该正确使用TabBarLayout', () => { render( ) // 检查TabBarLayout是否正确使用 const tabBarLayout = screen.getByTestId('tab-bar-layout') expect(tabBarLayout).toHaveAttribute('data-active-key', 'profile') }) test('应该正确使用Navbar', () => { render( ) // 检查Navbar是否正确使用 const navbar = screen.getByTestId('navbar') expect(navbar).toHaveAttribute('data-title', '个人中心') expect(navbar).toHaveAttribute('data-right-icon', 'i-heroicons-cog-6-tooth-20-solid') expect(navbar).toHaveAttribute('data-background-color', 'bg-primary') expect(navbar).toHaveAttribute('data-text-color', 'text-white') expect(navbar).toHaveAttribute('data-border', 'false') }) test('应该显示默认头像当用户无头像时', () => { // 模拟用户无头像的情况 const mockUseAuth = jest.requireMock('@/utils/auth').useAuth mockUseAuth.mockImplementation(() => ({ user: { ...mockUser, avatarFile: null }, logout: mockLogout, isLoading: false, updateUser: mockUpdateUser })) render( ) // 检查默认头像路径 const avatarUpload = screen.getByTestId('avatar-upload') expect(avatarUpload).toHaveAttribute('data-current-avatar', '/images/default_avatar.jpg') }) test('应该显示默认用户名', () => { // 模拟用户无用户名的情况 const mockUseAuth = jest.requireMock('@/utils/auth').useAuth mockUseAuth.mockImplementation(() => ({ user: { ...mockUser, username: '' }, logout: mockLogout, isLoading: false, updateUser: mockUpdateUser })) render( ) // 检查用户名显示为"普通用户" expect(screen.getByText('普通用户')).toBeInTheDocument() }) test('头像上传功能应该在默认头像状态下正常工作', async () => { // 模拟用户无头像的情况 const mockUseAuth = jest.requireMock('@/utils/auth').useAuth mockUseAuth.mockImplementation(() => ({ user: { ...mockUser, avatarFile: null }, logout: mockLogout, isLoading: false, updateUser: mockUpdateUser })) render( ) // 点击头像上传成功按钮 const uploadButton = screen.getByTestId('avatar-upload-button') fireEvent.click(uploadButton) // 检查上传成功处理 await waitFor(() => { expect(taroMock.showLoading).toHaveBeenCalledWith({ title: '更新头像...' }) expect(taroMock.hideLoading).toHaveBeenCalled() expect(taroMock.showToast).toHaveBeenCalledWith({ title: '头像更新成功', icon: 'success' }) expect(mockUpdateUser).toHaveBeenCalledWith({ ...mockUser, avatarFile: null, avatarFileId: 'test-file-id' }) }) }) test('应该正确处理常见问题弹窗的打开和关闭', () => { render( ) // 初始状态下弹窗应该是关闭的 const faqDialog = screen.getByTestId('faq-dialog') expect(faqDialog).toHaveAttribute('data-open', 'false') // 点击常见问题按钮打开弹窗 const faqButton = screen.getByTestId('faq-button') fireEvent.click(faqButton) // 检查弹窗状态变为打开 expect(faqDialog).toHaveAttribute('data-open', 'true') // 点击关闭按钮关闭弹窗 const closeButton = screen.getByTestId('faq-dialog-close') fireEvent.click(closeButton) // 检查弹窗状态变为关闭 expect(faqDialog).toHaveAttribute('data-open', 'false') }) test('常见问题弹窗应该显示正确的内容', () => { render( ) // 打开常见问题弹窗 const faqButton = screen.getByTestId('faq-button') fireEvent.click(faqButton) // 检查弹窗内容是否正确显示 const faqContent = screen.getByTestId('faq-content') expect(faqContent).toBeInTheDocument() expect(faqContent).toHaveTextContent('常见问题内容') }) })