| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436 |
- /**
- * 个人中心编辑资料功能单元测试
- * 测试头像和昵称编辑功能
- */
- import { render, screen, waitFor, fireEvent } from '@testing-library/react'
- import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
- import ProfilePage from '@/pages/profile/index'
- import { creditBalanceClient } from '@/api'
- import { useAuth } from '@/utils/auth'
- import Taro from '@tarojs/taro'
- // Mock API客户端
- jest.mock('@/api', () => ({
- creditBalanceClient: {
- me: {
- $get: jest.fn(),
- },
- },
- authClient: {
- me: {
- $get: jest.fn(),
- $put: jest.fn(),
- },
- },
- }))
- // Mock 认证hook
- jest.mock('@/utils/auth', () => ({
- useAuth: jest.fn(),
- }))
- // Mock TDesign组件 - 复用现有测试中的mock
- jest.mock('@/components/tdesign/user-center-card', () => ({
- __esModule: true,
- default: ({ avatar, nickname, isLoggedIn, onUserEdit, className }: any) => (
- <div data-testid="user-center-card" className={className}>
- <div data-testid="avatar">{avatar}</div>
- <div data-testid="nickname">{nickname}</div>
- <div data-testid="is-logged-in">{isLoggedIn ? '已登录' : '未登录'}</div>
- <button data-testid="edit-button" onClick={onUserEdit}>编辑</button>
- </div>
- ),
- }))
- jest.mock('@/components/tdesign/order-group', () => ({
- __esModule: true,
- default: ({ orderTagInfos, title, desc, onTopClick, onItemClick }: any) => (
- <div data-testid="order-group">
- <div data-testid="order-title">{title}</div>
- <div data-testid="order-desc">{desc}</div>
- <button data-testid="top-click" onClick={onTopClick}>查看全部</button>
- {orderTagInfos.map((item: any, index: number) => (
- <button key={index} data-testid={`order-item-${index}`} onClick={() => onItemClick(item)}>
- {item.title}
- </button>
- ))}
- </div>
- ),
- }))
- jest.mock('@/components/tdesign/cell-group', () => ({
- __esModule: true,
- default: ({ children }: any) => (
- <div data-testid="cell-group">{children}</div>
- ),
- }))
- jest.mock('@/components/tdesign/cell', () => ({
- __esModule: true,
- default: ({ title, bordered, onClick, noteSlot }: any) => (
- <div data-testid="cell" data-bordered={bordered}>
- <div data-testid="cell-title">{title}</div>
- <button data-testid="cell-click" onClick={onClick}>点击</button>
- <div data-testid="cell-note">{noteSlot}</div>
- </div>
- ),
- }))
- jest.mock('@/components/tdesign/popup', () => ({
- __esModule: true,
- default: ({ visible, placement, onClose, children }: any) => (
- visible ? (
- <div data-testid="popup" data-placement={placement}>
- {children}
- <button data-testid="popup-close" onClick={onClose}>关闭</button>
- </div>
- ) : null
- ),
- }))
- jest.mock('@/components/tdesign/icon', () => ({
- __esModule: true,
- default: ({ name, size, color }: any) => (
- <div data-testid="icon" data-name={name} data-size={size} data-color={color}>图标</div>
- ),
- }))
- // Mock AvatarUpload组件
- jest.mock('@/components/ui/avatar-upload', () => ({
- AvatarUpload: ({
- currentAvatar,
- onUploadSuccess,
- onUploadError,
- editable
- }: any) => (
- <div data-testid="avatar-upload" data-editable={editable}>
- <img src={currentAvatar} alt="头像" data-testid="avatar-image" />
- <button
- data-testid="upload-button"
- onClick={() => {
- // 模拟上传成功
- if (editable) {
- onUploadSuccess?.({
- fileUrl: 'https://example.com/new-avatar.jpg',
- fileId: 456,
- fileKey: 'avatars/new-avatar.jpg',
- bucketName: 'd8dai'
- })
- }
- }}
- >
- 上传头像
- </button>
- <button
- data-testid="upload-error-button"
- onClick={() => {
- onUploadError?.(new Error('模拟上传失败'))
- }}
- >
- 模拟上传失败
- </button>
- </div>
- ),
- }))
- // Mock Taro组件 - 输入框
- jest.mock('@tarojs/components', () => ({
- View: ({ children, ...props }: any) => <div {...props}>{children}</div>,
- Text: ({ children, ...props }: any) => <span {...props}>{children}</span>,
- ScrollView: ({ children, ...props }: any) => <div {...props}>{children}</div>,
- Input: ({ value, onInput, placeholder, maxlength, className }: any) => (
- <input
- data-testid="nickname-input"
- value={value}
- onChange={(e) => onInput?.({ detail: { value: e.target.value } })}
- placeholder={placeholder}
- maxLength={maxlength}
- className={className}
- />
- ),
- }))
- // Mock Button组件
- jest.mock('@/components/ui/button', () => ({
- Button: ({ variant, size, className, onClick, children }: any) => (
- <button
- data-testid={`button-${variant}`}
- data-size={size}
- className={className}
- onClick={onClick}
- >
- {children}
- </button>
- ),
- }))
- // 创建测试QueryClient
- const createTestQueryClient = () => new QueryClient({
- defaultOptions: {
- queries: { retry: false },
- mutations: { retry: false },
- },
- })
- // Mock CartContext
- jest.mock('@/contexts/CartContext', () => ({
- CartProvider: ({ children }: { children: React.ReactNode }) => <>{children}</>,
- useCart: () => ({
- cart: {
- items: [],
- totalAmount: 0,
- totalCount: 0,
- },
- addToCart: jest.fn(),
- removeFromCart: jest.fn(),
- updateQuantity: jest.fn(),
- clearCart: jest.fn(),
- isInCart: jest.fn(),
- getItemQuantity: jest.fn(),
- isLoading: false,
- }),
- }))
- // 测试包装器
- const TestWrapper = ({ children }: { children: React.ReactNode }) => (
- <QueryClientProvider client={createTestQueryClient()}>
- {children}
- </QueryClientProvider>
- )
- // 测试数据工厂
- const createTestUser = (overrides = {}) => ({
- id: 1,
- username: '测试用户',
- avatarFile: {
- id: 123,
- fullUrl: 'https://example.com/avatar.jpg'
- },
- ...overrides,
- })
- const createTestCreditBalance = (overrides = {}) => ({
- totalLimit: 1000,
- usedAmount: 200,
- availableAmount: 800,
- isEnabled: true,
- ...overrides,
- })
- describe('个人中心编辑资料功能测试', () => {
- beforeEach(() => {
- jest.clearAllMocks()
- // 设置默认认证状态
- ;(useAuth as jest.Mock).mockReturnValue({
- user: createTestUser(),
- logout: jest.fn(),
- isLoading: false,
- updateUser: jest.fn(),
- refreshUser: jest.fn(),
- })
- // 设置默认额度查询
- ;(creditBalanceClient.me.$get as jest.Mock).mockResolvedValue({
- status: 200,
- json: () => Promise.resolve(createTestCreditBalance({ usedAmount: 0 })),
- })
- })
- test('应该正确渲染个人中心页面', async () => {
- render(
- <TestWrapper>
- <ProfilePage />
- </TestWrapper>
- )
- // 验证页面标题
- await waitFor(() => {
- expect(screen.getByText('个人中心')).toBeInTheDocument()
- })
- // 验证用户信息显示
- expect(screen.getByTestId('user-center-card')).toBeInTheDocument()
- expect(screen.getByTestId('nickname')).toHaveTextContent('测试用户')
- expect(screen.getByTestId('avatar')).toHaveTextContent('https://example.com/avatar.jpg')
- })
- test('应该打开编辑资料弹窗并显示当前头像和昵称', async () => {
- render(
- <TestWrapper>
- <ProfilePage />
- </TestWrapper>
- )
- // 点击编辑按钮打开弹窗
- fireEvent.click(screen.getByTestId('edit-button'))
- // 验证弹窗显示
- expect(screen.getByTestId('popup')).toBeInTheDocument()
- // 验证头像显示正确
- const avatarImage = screen.getByTestId('avatar-image') as HTMLImageElement
- expect(avatarImage.src).toBe('https://example.com/avatar.jpg')
- // 验证昵称输入框显示正确
- const nicknameInput = screen.getByTestId('nickname-input') as HTMLInputElement
- expect(nicknameInput.value).toBe('测试用户')
- })
- test('头像上传成功应该调用updateUser和refreshUser', async () => {
- const mockUpdateUser = jest.fn()
- const mockRefreshUser = jest.fn()
- // 设置模拟函数
- ;(useAuth as jest.Mock).mockReturnValue({
- user: createTestUser(),
- logout: jest.fn(),
- isLoading: false,
- updateUser: mockUpdateUser,
- refreshUser: mockRefreshUser,
- })
- // 设置mock返回值,确保异步流程正常执行
- mockUpdateUser.mockResolvedValue(createTestUser())
- mockRefreshUser.mockResolvedValue(createTestUser())
- render(
- <TestWrapper>
- <ProfilePage />
- </TestWrapper>
- )
- // 打开编辑弹窗
- fireEvent.click(screen.getByTestId('edit-button'))
- // 点击上传按钮(模拟上传成功)
- fireEvent.click(screen.getByTestId('upload-button'))
- // 验证updateUser被调用,参数包含新的fileId
- await waitFor(() => {
- expect(mockUpdateUser).toHaveBeenCalledWith({ avatarFileId: 456 })
- })
- // 验证refreshUser被调用
- await waitFor(() => {
- expect(mockRefreshUser).toHaveBeenCalled()
- })
- })
- test('头像上传失败应该显示错误提示', async () => {
- // Mock Taro.showToast
- const mockShowToast = jest.fn()
- ;(Taro as any).showToast = mockShowToast
- render(
- <TestWrapper>
- <ProfilePage />
- </TestWrapper>
- )
- // 打开编辑弹窗
- fireEvent.click(screen.getByTestId('edit-button'))
- // 点击模拟上传失败按钮
- fireEvent.click(screen.getByTestId('upload-error-button'))
- // 验证错误提示被调用
- expect(mockShowToast).toHaveBeenCalledWith({
- title: '模拟上传失败',
- icon: 'none',
- duration: 3000,
- })
- })
- test('昵称修改应该调用updateUser和refreshUser', async () => {
- const mockUpdateUser = jest.fn()
- const mockRefreshUser = jest.fn()
- // 设置模拟函数
- ;(useAuth as jest.Mock).mockReturnValue({
- user: createTestUser(),
- logout: jest.fn(),
- isLoading: false,
- updateUser: mockUpdateUser,
- refreshUser: mockRefreshUser,
- })
- // 设置mock返回值,确保异步流程正常执行
- mockUpdateUser.mockResolvedValue(createTestUser())
- mockRefreshUser.mockResolvedValue(createTestUser())
- render(
- <TestWrapper>
- <ProfilePage />
- </TestWrapper>
- )
- // 打开编辑弹窗
- fireEvent.click(screen.getByTestId('edit-button'))
- // 修改昵称输入框
- const nicknameInput = screen.getByTestId('nickname-input')
- fireEvent.change(nicknameInput, { target: { value: '新昵称' } })
- // 点击保存按钮
- fireEvent.click(screen.getByTestId('button-default'))
- // 验证updateUser被调用,参数包含新昵称
- await waitFor(() => {
- expect(mockUpdateUser).toHaveBeenCalledWith({ username: '新昵称' })
- })
- // 验证refreshUser被调用
- await waitFor(() => {
- expect(mockRefreshUser).toHaveBeenCalled()
- })
- })
- test('同时修改头像和昵称应该调用updateUser包含两个字段', async () => {
- const mockUpdateUser = jest.fn()
- const mockRefreshUser = jest.fn()
- // 设置模拟函数
- ;(useAuth as jest.Mock).mockReturnValue({
- user: createTestUser(),
- logout: jest.fn(),
- isLoading: false,
- updateUser: mockUpdateUser,
- refreshUser: mockRefreshUser,
- })
- // 设置mock返回值,确保异步流程正常执行
- mockUpdateUser.mockResolvedValue(createTestUser())
- mockRefreshUser.mockResolvedValue(createTestUser())
- render(
- <TestWrapper>
- <ProfilePage />
- </TestWrapper>
- )
- // 打开编辑弹窗
- fireEvent.click(screen.getByTestId('edit-button'))
- // 上传新头像
- fireEvent.click(screen.getByTestId('upload-button'))
- // 修改昵称
- const nicknameInput = screen.getByTestId('nickname-input')
- fireEvent.change(nicknameInput, { target: { value: '新昵称' } })
- // 点击保存按钮
- fireEvent.click(screen.getByTestId('button-default'))
- // 验证updateUser被调用,参数包含两个字段
- await waitFor(() => {
- expect(mockUpdateUser).toHaveBeenCalledWith({
- avatarFileId: 456,
- username: '新昵称',
- })
- })
- // 验证refreshUser被调用
- await waitFor(() => {
- expect(mockRefreshUser).toHaveBeenCalled()
- })
- })
- })
|