import { fireEvent, render, screen, waitFor } from '@testing-library/react'; import { beforeEach, describe, expect, it, vi } from 'vitest'; import UserManagementPage from '../UserManagementPage.tsx'; const { listMock, createMock, updateMock, setPasswordMock, authState } = vi.hoisted(() => ({ listMock: vi.fn(), createMock: vi.fn(), updateMock: vi.fn(), setPasswordMock: vi.fn(), authState: { user: { id: 99, user_name: 'account_admin', display_name: 'account_admin', status: 'account_admin' as string, created_at: '', updated_at: '', }, }, })); vi.mock('../../services/apiClient.ts', () => ({ api: { users: { list: listMock, create: createMock, update: updateMock, setPassword: setPasswordMock, }, }, errorMessage: (err: unknown) => (err instanceof Error ? err.message : 'Terjadi kesalahan'), })); vi.mock('../../context/AuthContext.tsx', () => ({ useAuth: () => ({ user: authState.user, }), })); describe('UserManagementPage', () => { beforeEach(() => { listMock.mockReset(); createMock.mockReset(); updateMock.mockReset(); setPasswordMock.mockReset(); authState.user = { id: 99, user_name: 'account_admin', display_name: 'account_admin', status: 'account_admin', created_at: '', updated_at: '', }; listMock.mockResolvedValue([ { id: 1, user_name: 'gm1.login', display_name: 'gm1', status: 'gm', managed_by_name: 'buh1', created_at: '', updated_at: '', }, { id: 99, user_name: 'account_admin', display_name: 'account_admin', status: 'account_admin', managed_by_name: null, created_at: '', updated_at: '', }, ]); createMock.mockResolvedValue({ id: 2, user_name: 'newgm', display_name: 'newgm', status: 'gm', created_at: '', updated_at: '', }); updateMock.mockResolvedValue({}); setPasswordMock.mockResolvedValue({}); vi.spyOn(window, 'confirm').mockReturnValue(true); }); it('renders list from api.users.list', async () => { render(); expect(await screen.findByText('gm1')).toBeInTheDocument(); expect(screen.getByText('buh1')).toBeInTheDocument(); expect(screen.getAllByText('GM').length).toBeGreaterThan(0); }); it('submits create with password', async () => { render(); await screen.findByText('gm1'); fireEvent.change(screen.getByLabelText(/^Login$/i), { target: { value: 'newgm' }, }); fireEvent.change(screen.getByLabelText(/^Nama$/i), { target: { value: 'New GM' }, }); fireEvent.change(screen.getByLabelText(/Password sementara/i), { target: { value: 'temp123' }, }); fireEvent.click(screen.getByRole('button', { name: /Buat akun/i })); await waitFor(() => { expect(createMock).toHaveBeenCalledWith({ user_name: 'newgm', display_name: 'New GM', status: 'gm', password: 'temp123', }); }); }); it('deactivates user', async () => { render(); await screen.findByText('gm1'); fireEvent.click(screen.getByRole('button', { name: /Nonaktifkan/i })); await waitFor(() => { expect(updateMock).toHaveBeenCalledWith(1, { status: 'inactive' }); }); }); it('hides deactivate and role change on own row', async () => { render(); expect(await screen.findByText('(Anda)')).toBeInTheDocument(); expect(screen.getAllByRole('button', { name: /Nonaktifkan/i })).toHaveLength(1); expect(screen.getAllByRole('button', { name: /Ubah role/i })).toHaveLength(1); expect( screen.getByText(/Role & status akun sendiri tidak bisa diubah/i) ).toBeInTheDocument(); }); it('resets password', async () => { render(); await screen.findByText('gm1'); const resetBtn = screen.getAllByRole('button', { name: /Reset password/i })[0]; expect(resetBtn).toBeTruthy(); fireEvent.click(resetBtn!); fireEvent.change(screen.getByPlaceholderText(/Password baru/i), { target: { value: 'reset-me' }, }); fireEvent.click(screen.getByRole('button', { name: /^Reset$/i })); await waitFor(() => { expect(setPasswordMock).toHaveBeenCalledWith(1, 'reset-me'); }); }); });