PermissionGroupList.test.tsx 3.61 KB
// REQ-USR-001 / REQ-USR-002: PermissionGroupList 权限分类勾选列表单测(渲染/勾选集合/全选 indeterminate/回勾,BR10/BR11/D3)
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { renderShell } from './renderShell';
import PermissionGroupList from '../../src/pages/usr/UserDetail/PermissionGroupList';
import type { PermissionItem } from '../../src/api/types';

const PERMS: PermissionItem[] = [
  { id: 1, name: '默认显示', category: '基础' },
  { id: 2, name: '高级查看', category: '基础' },
  { id: 3, name: '导出', category: '报表' },
];

function setup(over: {
  permissions?: PermissionItem[];
  checkedIds?: number[];
} = {}) {
  const onToggle = vi.fn();
  const onToggleAll = vi.fn();
  renderShell(
    <PermissionGroupList
      permissions={over.permissions ?? PERMS}
      checkedIds={over.checkedIds ?? []}
      onToggle={onToggle}
      onToggleAll={onToggleAll}
    />,
    { preloadedAuth: { token: 't', user: { id: 1, sUserName: 'a', sUserType: 'x', sLanguage: '中文' } } },
  );
  return { onToggle, onToggleAll };
}

function rowCheckbox(id: number): HTMLInputElement {
  return screen.getByTestId('perm-check-' + id) as HTMLInputElement;
}

function allCheckbox(): HTMLInputElement {
  return screen.getByTestId('perm-check-all') as HTMLInputElement;
}

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

  it('renders header 权限分类 and one row per permission', () => {
    setup();
    expect(screen.getByText('权限分类')).toBeInTheDocument();
    expect(screen.getByText('默认显示')).toBeInTheDocument();
    expect(screen.getByText('高级查看')).toBeInTheDocument();
    expect(screen.getByText('导出')).toBeInTheDocument();
  });

  it('checked rows reflect checkedIds', () => {
    setup({ checkedIds: [1] });
    expect(rowCheckbox(1).checked).toBe(true);
    expect(rowCheckbox(2).checked).toBe(false);
    expect(rowCheckbox(3).checked).toBe(false);
  });

  it('toggling a row calls onToggle(id, checked)', async () => {
    const user = userEvent.setup();
    const { onToggle } = setup({ checkedIds: [1] });
    await user.click(rowCheckbox(2));
    expect(onToggle).toHaveBeenCalledWith(2, true);
    await user.click(rowCheckbox(1));
    expect(onToggle).toHaveBeenCalledWith(1, false);
  });

  it('header select-all checked when all selected; indeterminate when partial', () => {
    const { } = setup({ checkedIds: [1, 2, 3] });
    expect(allCheckbox().checked).toBe(true);
  });

  it('header indeterminate when partial; unchecked when none', () => {
    setup({ checkedIds: [1] });
    const all = allCheckbox();
    expect(all.checked).toBe(false);
    // AntD 半选用 aria-checked='mixed' 表达于 wrapper;input 仍未 checked
    const wrapper = all.closest('.ant-checkbox');
    expect(wrapper?.classList.contains('ant-checkbox-indeterminate')).toBe(true);
  });

  it('header toggle calls onToggleAll', async () => {
    const user = userEvent.setup();
    const { onToggleAll } = setup({ checkedIds: [] });
    await user.click(allCheckbox());
    expect(onToggleAll).toHaveBeenCalledWith(true);
  });

  it('header toggle off when all selected', async () => {
    const user = userEvent.setup();
    const { onToggleAll } = setup({ checkedIds: [1, 2, 3] });
    await user.click(allCheckbox());
    expect(onToggleAll).toHaveBeenCalledWith(false);
  });

  it('empty permissions renders empty list (no rows)', () => {
    setup({ permissions: [] });
    expect(screen.queryByTestId('perm-check-1')).toBeNull();
  });
});