UserDetailPage.test.tsx 4.97 KB
import { describe, it, expect, vi, beforeEach } from 'vitest'
import { render, screen, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { Provider } from 'react-redux'
import { MemoryRouter, Routes, Route } from 'react-router-dom'
import { configureStore } from '@reduxjs/toolkit'
import authReducer from '../store/slices/authSlice'
import tabsReducer from '../store/slices/tabsSlice'
import UserDetailPage from '../pages/usr/UserDetailPage'

vi.mock('../api/usr', () => ({
  getStaffs: vi.fn().mockResolvedValue([{ sId: 's1', sStaffName: '张三' }]),
  getPermissionGroups: vi.fn().mockResolvedValue([{ sId: 'pg1', sGroupCode: 'usr:create', sGroupName: '新增用户', sCategory: '用户管理' }]),
  createUser: vi.fn().mockResolvedValue({ userId: 'u2', userCode: 'UC002', username: 'bob' }),
  updateUser: vi.fn().mockResolvedValue({ userId: 'u1', username: 'alice', updatedAt: '2026-05-08T10:00:00' }),
  getUserList: vi.fn().mockResolvedValue({ total: 0, page: 1, pageSize: 20, list: [] }),
}))

vi.mock('../api/request', () => ({
  default: { get: vi.fn(), post: vi.fn(), put: vi.fn() },
}))

function makeStore() {
  const store = configureStore({ reducer: { auth: authReducer, tabs: tabsReducer } })
  store.dispatch({
    type: 'auth/setCredentials',
    payload: { accessToken: 'tok', refreshToken: 'ref', userInfo: { userId: 'u0', username: 'admin', userType: '超级管理员', language: '中文', brandId: 'b1' } },
  })
  return store
}

const rowData = {
  sId: 'u1', sUsername: 'alice', sUserCode: 'UC001', sUserType: '普通用户',
  sLanguage: '中文', bCanEditDoc: 0, bIsDisabled: 0, tLastLoginDate: null,
  sCreatorUsername: 'admin', tCreateDate: '2026-01-01 00:00:00',
  sStaffName: null, sDepartment: null,
}

function renderNew() {
  return render(
    <Provider store={makeStore()}>
      <MemoryRouter initialEntries={['/usr/users/new']}>
        <Routes>
          <Route path="/usr/users/new" element={<UserDetailPage />} />
          <Route path="/usr/users" element={<div>UserList</div>} />
        </Routes>
      </MemoryRouter>
    </Provider>
  )
}

function renderEdit(state = rowData) {
  return render(
    <Provider store={makeStore()}>
      <MemoryRouter initialEntries={[{ pathname: '/usr/users/u1', state }]}>
        <Routes>
          <Route path="/usr/users/:id" element={<UserDetailPage />} />
          <Route path="/usr/users" element={<div>UserList</div>} />
        </Routes>
      </MemoryRouter>
    </Provider>
  )
}

describe('UserDetailPage', () => {
  beforeEach(() => { vi.clearAllMocks() })

  it('newMode_showsSaveButton', () => {
    renderNew()
    expect(screen.getByRole('button', { name: /保存/ })).toBeInTheDocument()
  })

  it('newMode_showsPermissionGroupTab', async () => {
    renderNew()
    await waitFor(() => expect(screen.getByText('新增用户')).toBeInTheDocument())
  })

  it('editMode_showsUsernameReadonly', async () => {
    renderEdit()
    await waitFor(() => expect(screen.getByText('alice')).toBeInTheDocument())
  })

  it('editMode_noState_redirectsToList', async () => {
    render(
      <Provider store={makeStore()}>
        <MemoryRouter initialEntries={['/usr/users/u1']}>
          <Routes>
            <Route path="/usr/users/:id" element={<UserDetailPage />} />
            <Route path="/usr/users" element={<div>UserList</div>} />
          </Routes>
        </MemoryRouter>
      </Provider>
    )
    await waitFor(() => expect(screen.getByText('UserList')).toBeInTheDocument())
  })

  it('newMode_save_callsCreateUser', async () => {
    const { createUser } = await import('../api/usr')
    renderNew()
    await userEvent.type(screen.getByPlaceholderText('请输入用户号'), 'UC002')
    await userEvent.type(screen.getByPlaceholderText('请输入用户名'), 'bob')
    await userEvent.click(screen.getByRole('button', { name: /保存/ }))
    await waitFor(() => expect(vi.mocked(createUser)).toHaveBeenCalledWith(
      expect.objectContaining({ userCode: 'UC002', username: 'bob' })
    ))
  })

  it('editMode_save_callsUpdateUser', async () => {
    const { updateUser } = await import('../api/usr')
    renderEdit()
    await waitFor(() => expect(screen.getByText('alice')).toBeInTheDocument())
    await userEvent.click(screen.getByRole('button', { name: /保存/ }))
    await waitFor(() => expect(vi.mocked(updateUser)).toHaveBeenCalledWith(
      'u1',
      expect.objectContaining({ userType: '普通用户' })
    ))
  })

  it('permCheckbox_togglesSelection', async () => {
    renderNew()
    await waitFor(() => expect(screen.getByText('新增用户')).toBeInTheDocument())
    const cb = screen.getByRole('checkbox')
    expect(cb).not.toBeChecked()
    await userEvent.click(cb)
    expect(cb).toBeChecked()
    await userEvent.click(cb)
    expect(cb).not.toBeChecked()
  })

  it('cancelButton_navigatesToList', async () => {
    renderNew()
    await userEvent.click(screen.getByRole('button', { name: /取消/ }))
    await waitFor(() => expect(screen.getByText('UserList')).toBeInTheDocument())
  })
})