import { describe, it, expect, vi, beforeEach } from 'vitest'; import { render as rtlRender, screen, fireEvent, waitFor, } from '@testing-library/react'; import React from 'react'; import { MantineProvider } from '@mantine/core'; import AccountSettings from './AccountSettings'; // Mock the auth context const mockUser = { id: 1, email: 'test@example.com', displayName: 'Test User', role: 'editor' as const, }; const mockRefreshUser = vi.fn(); vi.mock('../../../contexts/AuthContext', () => ({ useAuth: () => ({ user: mockUser, refreshUser: mockRefreshUser, }), })); // Mock the profile settings hook const mockUpdateProfile = vi.fn(); vi.mock('../../../hooks/useProfileSettings', () => ({ useProfileSettings: () => ({ loading: false, updateProfile: mockUpdateProfile, }), })); // Mock notifications vi.mock('@mantine/notifications', () => ({ notifications: { show: vi.fn(), }, })); // Mock the sub-components vi.mock('./ProfileSettings', () => ({ default: ({ settings, onInputChange, }: { settings: { displayName?: string; email?: string; }; onInputChange: (field: string, value: string) => void; }) => (
onInputChange('displayName', e.target.value)} /> onInputChange('email', e.target.value)} />
), })); vi.mock('./SecuritySettings', () => ({ default: ({ settings, onInputChange, }: { settings: { currentPassword?: string; newPassword?: string; }; onInputChange: (field: string, value: string) => void; }) => (
onInputChange('currentPassword', e.target.value)} /> onInputChange('newPassword', e.target.value)} />
), })); vi.mock('./DangerZoneSettings', () => ({ default: () =>
Danger Zone
, })); vi.mock('../../modals/account/EmailPasswordModal', () => ({ default: ({ opened, onConfirm, }: { opened: boolean; onConfirm: (password: string) => void; }) => opened ? (
) : null, })); // Helper wrapper component for testing const TestWrapper = ({ children }: { children: React.ReactNode }) => ( {children} ); // Custom render function const render = (ui: React.ReactElement) => { return rtlRender(ui, { wrapper: TestWrapper }); }; describe('AccountSettings', () => { beforeEach(() => { vi.clearAllMocks(); mockUpdateProfile.mockResolvedValue(mockUser); mockRefreshUser.mockResolvedValue(undefined); }); it('renders modal with all sections', () => { render(); expect(screen.getByText('Account Settings')).toBeInTheDocument(); expect(screen.getByTestId('profile-settings')).toBeInTheDocument(); expect(screen.getByTestId('security-settings')).toBeInTheDocument(); expect(screen.getByTestId('danger-zone-settings')).toBeInTheDocument(); }); it('shows unsaved changes badge when settings are modified', () => { render(); const displayNameInput = screen.getByTestId('display-name-input'); fireEvent.change(displayNameInput, { target: { value: 'Updated Name' } }); expect(screen.getByText('Unsaved Changes')).toBeInTheDocument(); }); it('enables save button when there are changes', () => { render(); const saveButton = screen.getByRole('button', { name: 'Save Changes' }); expect(saveButton).toBeDisabled(); const displayNameInput = screen.getByTestId('display-name-input'); fireEvent.change(displayNameInput, { target: { value: 'Updated Name' } }); expect(saveButton).not.toBeDisabled(); }); it('saves profile changes successfully', async () => { const mockOnClose = vi.fn(); render(); const displayNameInput = screen.getByTestId('display-name-input'); fireEvent.change(displayNameInput, { target: { value: 'Updated Name' } }); const saveButton = screen.getByRole('button', { name: 'Save Changes' }); fireEvent.click(saveButton); await waitFor(() => { expect(mockUpdateProfile).toHaveBeenCalledWith( expect.objectContaining({ displayName: 'Updated Name' }) ); }); await waitFor(() => { expect(mockRefreshUser).toHaveBeenCalled(); }); await waitFor(() => { expect(mockOnClose).toHaveBeenCalled(); }); }); it('opens email confirmation modal for email changes', () => { render(); const emailInput = screen.getByTestId('email-input'); fireEvent.change(emailInput, { target: { value: 'new@example.com' } }); const saveButton = screen.getByRole('button', { name: 'Save Changes' }); fireEvent.click(saveButton); expect(screen.getByTestId('email-password-modal')).toBeInTheDocument(); }); it('completes email change with password confirmation', async () => { const mockOnClose = vi.fn(); render(); const emailInput = screen.getByTestId('email-input'); fireEvent.change(emailInput, { target: { value: 'new@example.com' } }); const saveButton = screen.getByRole('button', { name: 'Save Changes' }); fireEvent.click(saveButton); const confirmButton = screen.getByTestId('confirm-email'); fireEvent.click(confirmButton); await waitFor(() => { expect(mockUpdateProfile).toHaveBeenCalledWith( expect.objectContaining({ email: 'new@example.com', currentPassword: 'test-password', }) ); }); await waitFor(() => { expect(mockOnClose).toHaveBeenCalled(); }); }); it('closes modal when cancel is clicked', () => { const mockOnClose = vi.fn(); render(); const cancelButton = screen.getByRole('button', { name: 'Cancel' }); fireEvent.click(cancelButton); expect(mockOnClose).toHaveBeenCalled(); }); it('does not render when closed', () => { render(); expect(screen.queryByText('Account Settings')).not.toBeInTheDocument(); }); });