/** @jest-environment jsdom */ import { fireEvent, render, screen, waitFor, within } from '@testing-library/react' import { PerformanceSurveyAdminPeople, type PerformanceSurveyAdminRequest, } from './PerformanceSurveyAdminPeople' const groups = [{ id: 'group-1', name: '品牌设计师', status: 'ready' as const, memberCount: 10, }] function renderPeople(request: PerformanceSurveyAdminRequest) { const onError = jest.fn() const onSuccess = jest.fn() render( , ) return { onError, onSuccess } } describe('PerformanceSurveyAdminPeople', () => { test('使用每页 50 人,并同时提供桌面表格和移动端卡片', async () => { const request = jest.fn(async () => ({ items: [{ id: 'employee-1', employeeId: 'employee-1', displayName: '张三', departmentPath: '集团/品牌中心', positionTitle: '设计师', participation: 'unassigned', }], total: 1, })) as unknown as jest.MockedFunction renderPeople(request) expect(screen.getByRole('status')).toHaveTextContent('正在加载人员列表') await waitFor(() => expect(within(screen.getByTestId('people-mobile-cards')).getByText('张三')).toBeInTheDocument()) expect(screen.getByTestId('people-desktop-table')).toBeInTheDocument() expect(screen.getByTestId('people-mobile-cards')).toBeInTheDocument() expect(request).toHaveBeenCalledWith(expect.stringContaining('pageSize=50')) expect(screen.getByRole('status')).toHaveTextContent('人员列表已更新,共 1 人') }) test('选择跨页保留,并在固定批量栏一次提交全部人员', async () => { const request = jest.fn(async (path: string, _options?: RequestInit) => { if (path.startsWith('/admin/position-assignments?')) { const page = new URL(path, 'https://example.test').searchParams.get('page') return { items: page === '2' ? [{ id: 'employee-2', employeeId: 'employee-2', displayName: '李四', departmentName: '摄影中心', positionTitle: '摄影师', participation: 'unassigned', }] : [{ id: 'employee-1', employeeId: 'employee-1', displayName: '张三', departmentName: '品牌中心', positionTitle: '设计师', participation: 'unassigned', }], total: 51, } } return {} }) as unknown as jest.MockedFunction const { onSuccess } = renderPeople(request) await waitFor(() => expect(screen.getByLabelText('选择 张三')).toBeInTheDocument()) fireEvent.click(screen.getByLabelText('选择 张三')) expect(screen.getByRole('region', { name: '批量分配所选人员' })).toHaveTextContent('已选择 1 人') fireEvent.click(screen.getByRole('button', { name: '下一页' })) await waitFor(() => expect(screen.getByLabelText('选择 李四')).toBeInTheDocument()) fireEvent.click(screen.getByLabelText('选择 李四')) expect(screen.getByRole('region', { name: '批量分配所选人员' })).toHaveTextContent('已选择 2 人') fireEvent.change(screen.getByLabelText('批量分配操作'), { target: { value: 'group-1' } }) expect(screen.getByPlaceholderText('本次调整原因(选填)')).toBeInTheDocument() fireEvent.click(screen.getByRole('button', { name: '应用分配' })) await waitFor(() => { const call = request.mock.calls.find(([path, options]) => ( path === '/admin/position-assignments' && options?.method === 'POST' )) expect(call).toBeDefined() expect(JSON.parse(String(call?.[1]?.body))).toEqual(expect.objectContaining({ employeeIds: ['employee-1', 'employee-2'], positionGroupId: 'group-1', reason: '', })) }) expect(onSuccess).toHaveBeenCalledWith('已为 2 人分配岗位') }) test('清楚区分主部门、兼任部门、职级和钉钉身份', async () => { const request = jest.fn(async () => ({ items: [{ id: 'employee-1', employeeId: 'employee-1', displayName: '忆江', departmentName: '综合支持部', departmentPath: '集团/品牌中心/综合支持部', departmentMemberships: [{ id: 'membership-primary', sourceDeptId: 'dept-primary', departmentName: '综合支持部', departmentPath: '集团/品牌中心/综合支持部', isPrimary: true, syncedAt: '2026-07-27T01:00:00.000Z', }, { id: 'membership-photo', sourceDeptId: 'dept-photo', departmentName: '摄影中心', departmentPath: '集团/摄影中心', isPrimary: false, syncedAt: '2026-07-27T01:00:00.000Z', }, { id: 'membership-support', sourceDeptId: 'dept-support', departmentName: '综合支持二部', departmentPath: '集团/综合支持二部', isPrimary: false, syncedAt: '2026-07-27T01:00:00.000Z', }, { id: 'membership-ai', sourceDeptId: 'dept-ai', departmentName: 'AI 中心', departmentPath: '集团/AI 中心', isPrimary: false, syncedAt: '2026-07-27T01:00:00.000Z', }], positionTitle: '视觉设计师', positionLevel: '4', levelType: 'P', isLeadership: false, isDingtalkAdmin: true, isDingtalkBoss: false, isDingtalkSenior: true, participation: 'assigned', positionGroupName: '品牌设计师', }, { id: 'employee-2', employeeId: 'employee-2', displayName: '元泽', departmentName: '品牌中心', positionTitle: '负责人', positionLevel: '1', levelType: 'M', isLeadership: true, isDingtalkAdmin: true, isDingtalkBoss: true, participation: 'assigned', positionGroupName: '品牌管理', }], total: 2, })) as unknown as jest.MockedFunction renderPeople(request) const desktop = screen.getByTestId('people-desktop-table') await waitFor(() => expect(within(desktop).getByText('忆江')).toBeInTheDocument()) expect(screen.getByPlaceholderText('姓名、部门、职位或职级')).toBeInTheDocument() expect(within(desktop).getByText('集团/品牌中心/综合支持部')).toBeInTheDocument() expect(within(desktop).getByText('摄影中心、综合支持二部 等 3 个部门')).toBeInTheDocument() expect(within(desktop).getByTitle('摄影中心、综合支持二部、AI 中心')).toBeInTheDocument() expect(within(desktop).getByText('P4')).toBeInTheDocument() expect(within(desktop).getByText('M1')).toBeInTheDocument() expect(within(desktop).getByText('管理岗')).toBeInTheDocument() expect(within(desktop).getAllByText('钉钉管理员')).toHaveLength(2) expect(within(desktop).getByText('钉钉企业负责人')).toBeInTheDocument() expect(within(desktop).getByText('钉钉高管')).toBeInTheDocument() const mobile = screen.getByTestId('people-mobile-cards') expect(within(mobile).getAllByText('主部门')).toHaveLength(2) expect(within(mobile).getByText('摄影中心、综合支持二部 等 3 个部门')).toBeInTheDocument() expect(within(mobile).getByText('P4')).toBeInTheDocument() expect(within(mobile).getAllByText('钉钉管理员')).toHaveLength(2) expect(screen.queryByText('系统管理员')).not.toBeInTheDocument() }) test('网络失败显示中文可恢复提示', async () => { const request = jest.fn() .mockRejectedValueOnce(new Error('Failed to fetch')) .mockResolvedValueOnce({ items: [], total: 0 }) as unknown as jest.MockedFunction renderPeople(request) expect(await screen.findByRole('alert')).toHaveTextContent('暂时无法加载人员,请检查网络后重试。') expect(screen.queryByText('Failed to fetch')).not.toBeInTheDocument() fireEvent.click(screen.getByRole('button', { name: '重新加载' })) await waitFor(() => expect(screen.getByRole('status')).toHaveTextContent('人员列表已更新,共 0 人')) }) })