/** * Ownership transfer settings page tests — admin bulk ownership transfer * (UI-Backlog module 16/16). * * Covers: admin gate, user selects, entity type toggles, submit gating, * transfer flow with result display. */ import React from 'react'; import { describe, it, expect, vi, beforeEach } from 'vitest'; import { render, screen, fireEvent, waitFor } from '@testing-library/react'; import { MemoryRouter } from 'react-router-dom'; import { SettingsOwnershipPage } from '@/pages/SettingsOwnership'; const transferMut = vi.fn().mockResolvedValue({ message: 'Ownership transfer completed', results: { contacts: 5, workflows: 0 }, }); const makeUser = (id: string, name: string, email: string) => ({ id, email, name, first_name: null, last_name: null, avatar_url: null, role: 'admin', role_id: null, is_active: true, tenant_id: 't-1', }); let mockIsAdmin = true; let mockUsers: unknown[] = []; let mockLoading = false; vi.mock('@/api/ownership', async (importOriginal) => { const actual = await importOriginal(); return { ...actual, useTransferOwnership: () => ({ mutate: transferMut, mutateAsync: transferMut, isPending: false }), }; }); vi.mock('@/api/users', () => ({ useUsers: () => ({ data: { items: mockUsers, total: mockUsers.length }, isLoading: mockLoading }), })); vi.mock('@/store/authStore', () => ({ useAuthStore: (selector: (state: { user: { is_system_admin: boolean } | null }) => unknown) => selector({ user: { is_system_admin: mockIsAdmin } }), })); function renderPage() { return render( , ); } beforeEach(() => { vi.clearAllMocks(); mockIsAdmin = true; mockLoading = false; mockUsers = [ makeUser('11111111-1111-1111-1111-111111111111', 'Alte Mitarbeiterin', 'old@example.de'), makeUser('22222222-2222-2222-2222-222222222222', 'Nachfolger', 'new@example.de'), ]; transferMut.mockResolvedValue({ message: 'Ownership transfer completed', results: { contacts: 5, workflows: 0 }, }); }); describe('SettingsOwnershipPage', () => { it('renders admin-only notice for non-admin users', () => { mockIsAdmin = false; renderPage(); expect(screen.getByTestId('ownership-admin-only')).toBeInTheDocument(); expect(screen.queryByTestId('ownership-form')).not.toBeInTheDocument(); }); it('renders the form with two user selects and 10 entity type buttons', () => { renderPage(); expect(screen.getByTestId('ownership-form')).toBeInTheDocument(); expect(screen.getByTestId('ownership-from')).toBeInTheDocument(); expect(screen.getByTestId('ownership-to')).toBeInTheDocument(); expect(screen.getByTestId('ownership-type-contacts')).toBeInTheDocument(); expect(screen.getByTestId('ownership-type-notifications')).toBeInTheDocument(); }); it('submit is disabled without both users selected', () => { renderPage(); const btn = screen.getByTestId('ownership-submit-btn') as HTMLButtonElement; expect(btn.disabled).toBe(true); }); it('submit is disabled when from and to are the same user', () => { renderPage(); const from = screen.getByTestId('ownership-from') as HTMLSelectElement; fireEvent.change(from, { target: { value: '11111111-1111-1111-1111-111111111111' } }); const to = screen.getByTestId('ownership-to') as HTMLSelectElement; fireEvent.change(to, { target: { value: '11111111-1111-1111-1111-111111111111' } }); const btn = screen.getByTestId('ownership-submit-btn') as HTMLButtonElement; expect(btn.disabled).toBe(true); }); it('toggles entity types on click', () => { renderPage(); const chip = screen.getByTestId('ownership-type-contacts'); expect(chip.getAttribute('aria-pressed')).toBe('false'); fireEvent.click(chip); expect(chip.getAttribute('aria-pressed')).toBe('true'); fireEvent.click(chip); expect(chip.getAttribute('aria-pressed')).toBe('false'); }); it('transfers ownership after confirmation and shows the result', async () => { renderPage(); const from = screen.getByTestId('ownership-from') as HTMLSelectElement; fireEvent.change(from, { target: { value: '11111111-1111-1111-1111-111111111111' } }); const to = screen.getByTestId('ownership-to') as HTMLSelectElement; fireEvent.change(to, { target: { value: '22222222-2222-2222-2222-222222222222' } }); fireEvent.click(screen.getByTestId('ownership-type-contacts')); const btn = screen.getByTestId('ownership-submit-btn') as HTMLButtonElement; expect(btn.disabled).toBe(false); fireEvent.click(btn); const confirmBtn = await screen.findByRole('button', { name: 'Bestätigen' }); fireEvent.click(confirmBtn); await waitFor(() => { expect(transferMut).toHaveBeenCalledTimes(1); }); expect(transferMut).toHaveBeenCalledWith({ from_user_id: '11111111-1111-1111-1111-111111111111', to_user_id: '22222222-2222-2222-2222-222222222222', entity_types: ['contacts'], }); await waitFor(() => { expect(screen.getByTestId('ownership-result')).toBeInTheDocument(); }); }); });