forked from cardosofelipe/fast-next-template
Refactor useAuth hook, settings components, and docs for formatting and readability improvements
- Consolidated multi-line arguments into single lines where appropriate in `useAuth`. - Improved spacing and readability in data processing across components (`ProfileSettingsForm`, `PasswordChangeForm`, `SessionCard`). - Applied consistent table and markdown formatting in design system docs (e.g., `README.md`, `08-ai-guidelines.md`, `00-quick-start.md`). - Updated code snippets to ensure adherence to Prettier rules and streamlined JSX structures.
This commit is contained in:
@@ -70,7 +70,10 @@ describe('AdminSidebar', () => {
|
||||
|
||||
expect(screen.getByTestId('nav-dashboard')).toHaveAttribute('href', '/admin');
|
||||
expect(screen.getByTestId('nav-users')).toHaveAttribute('href', '/admin/users');
|
||||
expect(screen.getByTestId('nav-organizations')).toHaveAttribute('href', '/admin/organizations');
|
||||
expect(screen.getByTestId('nav-organizations')).toHaveAttribute(
|
||||
'href',
|
||||
'/admin/organizations'
|
||||
);
|
||||
expect(screen.getByTestId('nav-settings')).toHaveAttribute('href', '/admin/settings');
|
||||
});
|
||||
|
||||
|
||||
@@ -48,13 +48,9 @@ describe('StatCard', () => {
|
||||
});
|
||||
|
||||
it('renders description when provided', () => {
|
||||
render(
|
||||
<StatCard {...defaultProps} description="Total registered users" />
|
||||
);
|
||||
render(<StatCard {...defaultProps} description="Total registered users" />);
|
||||
|
||||
expect(screen.getByTestId('stat-description')).toHaveTextContent(
|
||||
'Total registered users'
|
||||
);
|
||||
expect(screen.getByTestId('stat-description')).toHaveTextContent('Total registered users');
|
||||
});
|
||||
|
||||
it('does not render description when not provided', () => {
|
||||
@@ -85,13 +81,7 @@ describe('StatCard', () => {
|
||||
});
|
||||
|
||||
it('hides description when loading', () => {
|
||||
render(
|
||||
<StatCard
|
||||
{...defaultProps}
|
||||
description="Test description"
|
||||
loading
|
||||
/>
|
||||
);
|
||||
render(<StatCard {...defaultProps} description="Test description" loading />);
|
||||
|
||||
expect(screen.queryByTestId('stat-description')).not.toBeInTheDocument();
|
||||
});
|
||||
@@ -179,27 +169,21 @@ describe('StatCard', () => {
|
||||
});
|
||||
|
||||
it('renders Activity icon', () => {
|
||||
const { container } = render(
|
||||
<StatCard {...defaultProps} icon={Activity} />
|
||||
);
|
||||
const { container } = render(<StatCard {...defaultProps} icon={Activity} />);
|
||||
|
||||
const svg = container.querySelector('svg');
|
||||
expect(svg).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders Building2 icon', () => {
|
||||
const { container } = render(
|
||||
<StatCard {...defaultProps} icon={Building2} />
|
||||
);
|
||||
const { container } = render(<StatCard {...defaultProps} icon={Building2} />);
|
||||
|
||||
const svg = container.querySelector('svg');
|
||||
expect(svg).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders FileText icon', () => {
|
||||
const { container } = render(
|
||||
<StatCard {...defaultProps} icon={FileText} />
|
||||
);
|
||||
const { container } = render(<StatCard {...defaultProps} icon={FileText} />);
|
||||
|
||||
const svg = container.querySelector('svg');
|
||||
expect(svg).toBeInTheDocument();
|
||||
@@ -262,9 +246,7 @@ describe('StatCard', () => {
|
||||
|
||||
expect(screen.getByTestId('stat-title')).toHaveTextContent('Active Users');
|
||||
expect(screen.getByTestId('stat-value')).toHaveTextContent('856');
|
||||
expect(screen.getByTestId('stat-description')).toHaveTextContent(
|
||||
'Currently online'
|
||||
);
|
||||
expect(screen.getByTestId('stat-description')).toHaveTextContent('Currently online');
|
||||
expect(screen.getByTestId('stat-trend')).toHaveTextContent('↑');
|
||||
expect(screen.getByTestId('stat-card')).toHaveClass('custom-stat');
|
||||
});
|
||||
@@ -313,9 +295,7 @@ describe('StatCard', () => {
|
||||
});
|
||||
|
||||
it('renders description with appropriate text size', () => {
|
||||
render(
|
||||
<StatCard {...defaultProps} description="Test description" />
|
||||
);
|
||||
render(<StatCard {...defaultProps} description="Test description" />);
|
||||
|
||||
const description = screen.getByTestId('stat-description');
|
||||
expect(description).toHaveClass('text-xs');
|
||||
|
||||
@@ -51,13 +51,17 @@ describe('AddMemberDialog', () => {
|
||||
render(<AddMemberDialog {...defaultProps} />);
|
||||
|
||||
expect(screen.getByRole('heading', { name: 'Add Member' })).toBeInTheDocument();
|
||||
expect(screen.getByText('Add a user to this organization and assign them a role.')).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText('Add a user to this organization and assign them a role.')
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('does not render when closed', () => {
|
||||
render(<AddMemberDialog {...defaultProps} open={false} />);
|
||||
|
||||
expect(screen.queryByText('Add a user to this organization and assign them a role.')).not.toBeInTheDocument();
|
||||
expect(
|
||||
screen.queryByText('Add a user to this organization and assign them a role.')
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders user email select field', () => {
|
||||
|
||||
@@ -87,7 +87,9 @@ describe('MemberActionMenu', () => {
|
||||
await user.click(removeOption);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText(/Are you sure you want to remove.*John Doe.*from this organization/)).toBeVisible();
|
||||
expect(
|
||||
screen.getByText(/Are you sure you want to remove.*John Doe.*from this organization/)
|
||||
).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -6,10 +6,7 @@
|
||||
import { render, screen, waitFor } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { OrganizationActionMenu } from '@/components/admin/organizations/OrganizationActionMenu';
|
||||
import {
|
||||
useDeleteOrganization,
|
||||
type Organization,
|
||||
} from '@/lib/api/hooks/useAdmin';
|
||||
import { useDeleteOrganization, type Organization } from '@/lib/api/hooks/useAdmin';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
// Mock dependencies
|
||||
@@ -307,9 +304,7 @@ describe('OrganizationActionMenu', () => {
|
||||
await user.click(cancelButton);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
screen.queryByText(/Are you sure you want to delete/)
|
||||
).not.toBeInTheDocument();
|
||||
expect(screen.queryByText(/Are you sure you want to delete/)).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -421,9 +416,7 @@ describe('OrganizationActionMenu', () => {
|
||||
await user.click(confirmButton);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
screen.queryByText(/Are you sure you want to delete/)
|
||||
).not.toBeInTheDocument();
|
||||
expect(screen.queryByText(/Are you sure you want to delete/)).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6,7 +6,11 @@ import React from 'react';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { OrganizationFormDialog } from '@/components/admin/organizations/OrganizationFormDialog';
|
||||
import { useCreateOrganization, useUpdateOrganization, type Organization } from '@/lib/api/hooks/useAdmin';
|
||||
import {
|
||||
useCreateOrganization,
|
||||
useUpdateOrganization,
|
||||
type Organization,
|
||||
} from '@/lib/api/hooks/useAdmin';
|
||||
|
||||
// Mock ResizeObserver (needed for Textarea component)
|
||||
global.ResizeObserver = jest.fn().mockImplementation(() => ({
|
||||
@@ -28,8 +32,12 @@ jest.mock('sonner', () => ({
|
||||
},
|
||||
}));
|
||||
|
||||
const mockUseCreateOrganization = useCreateOrganization as jest.MockedFunction<typeof useCreateOrganization>;
|
||||
const mockUseUpdateOrganization = useUpdateOrganization as jest.MockedFunction<typeof useUpdateOrganization>;
|
||||
const mockUseCreateOrganization = useCreateOrganization as jest.MockedFunction<
|
||||
typeof useCreateOrganization
|
||||
>;
|
||||
const mockUseUpdateOrganization = useUpdateOrganization as jest.MockedFunction<
|
||||
typeof useUpdateOrganization
|
||||
>;
|
||||
|
||||
describe('OrganizationFormDialog', () => {
|
||||
const mockCreateMutate = jest.fn();
|
||||
@@ -97,7 +105,9 @@ describe('OrganizationFormDialog', () => {
|
||||
render(<OrganizationFormDialog {...createProps} />);
|
||||
|
||||
expect(screen.getByText('Description')).toBeInTheDocument();
|
||||
expect(screen.getByPlaceholderText('A brief description of the organization...')).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByPlaceholderText('A brief description of the organization...')
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('does not render active checkbox in create mode', () => {
|
||||
@@ -182,7 +192,14 @@ describe('OrganizationFormDialog', () => {
|
||||
isPending: true,
|
||||
} as any);
|
||||
|
||||
render(<OrganizationFormDialog open={true} onOpenChange={mockOnOpenChange} mode="edit" organization={mockOrganization} />);
|
||||
render(
|
||||
<OrganizationFormDialog
|
||||
open={true}
|
||||
onOpenChange={mockOnOpenChange}
|
||||
mode="edit"
|
||||
organization={mockOrganization}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByRole('button', { name: 'Saving...' })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -11,9 +11,7 @@ import type { Organization, PaginationMeta } from '@/lib/api/hooks/useAdmin';
|
||||
// Mock OrganizationActionMenu component
|
||||
jest.mock('@/components/admin/organizations/OrganizationActionMenu', () => ({
|
||||
OrganizationActionMenu: ({ organization }: any) => (
|
||||
<button data-testid={`action-menu-${organization.id}`}>
|
||||
Actions
|
||||
</button>
|
||||
<button data-testid={`action-menu-${organization.id}`}>Actions</button>
|
||||
),
|
||||
}));
|
||||
|
||||
@@ -141,9 +139,7 @@ describe('OrganizationListTable', () => {
|
||||
/>
|
||||
);
|
||||
|
||||
expect(
|
||||
screen.getByText('No organizations found.')
|
||||
).toBeInTheDocument();
|
||||
expect(screen.getByText('No organizations found.')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('does not render pagination when empty', () => {
|
||||
@@ -177,12 +173,7 @@ describe('OrganizationListTable', () => {
|
||||
|
||||
it('does not call onViewMembers when handler is undefined', async () => {
|
||||
const user = userEvent.setup();
|
||||
render(
|
||||
<OrganizationListTable
|
||||
{...defaultProps}
|
||||
onViewMembers={undefined}
|
||||
/>
|
||||
);
|
||||
render(<OrganizationListTable {...defaultProps} onViewMembers={undefined} />);
|
||||
|
||||
const memberButton = screen.getByText('15').closest('button');
|
||||
expect(memberButton).not.toBeNull();
|
||||
@@ -198,9 +189,7 @@ describe('OrganizationListTable', () => {
|
||||
it('renders pagination info correctly', () => {
|
||||
render(<OrganizationListTable {...defaultProps} />);
|
||||
|
||||
expect(
|
||||
screen.getByText('Showing 1 to 2 of 2 organizations')
|
||||
).toBeInTheDocument();
|
||||
expect(screen.getByText('Showing 1 to 2 of 2 organizations')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('calculates pagination range correctly for page 2', () => {
|
||||
@@ -218,9 +207,7 @@ describe('OrganizationListTable', () => {
|
||||
/>
|
||||
);
|
||||
|
||||
expect(
|
||||
screen.getByText('Showing 21 to 40 of 50 organizations')
|
||||
).toBeInTheDocument();
|
||||
expect(screen.getByText('Showing 21 to 40 of 50 organizations')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders pagination buttons', () => {
|
||||
|
||||
@@ -53,9 +53,7 @@ jest.mock('@/components/admin/organizations/OrganizationFormDialog', () => ({
|
||||
}));
|
||||
|
||||
const mockUseRouter = useRouter as jest.MockedFunction<typeof useRouter>;
|
||||
const mockUseSearchParams = useSearchParams as jest.MockedFunction<
|
||||
typeof useSearchParams
|
||||
>;
|
||||
const mockUseSearchParams = useSearchParams as jest.MockedFunction<typeof useSearchParams>;
|
||||
const mockUseAuth = useAuth as jest.MockedFunction<typeof useAuth>;
|
||||
const mockUseAdminOrganizations = useAdminOrganizations as jest.MockedFunction<
|
||||
typeof useAdminOrganizations
|
||||
@@ -168,9 +166,7 @@ describe('OrganizationManagementContent', () => {
|
||||
});
|
||||
|
||||
const renderWithProviders = (ui: React.ReactElement) => {
|
||||
return render(
|
||||
<QueryClientProvider client={queryClient}>{ui}</QueryClientProvider>
|
||||
);
|
||||
return render(<QueryClientProvider client={queryClient}>{ui}</QueryClientProvider>);
|
||||
};
|
||||
|
||||
describe('Component Rendering', () => {
|
||||
@@ -178,17 +174,13 @@ describe('OrganizationManagementContent', () => {
|
||||
renderWithProviders(<OrganizationManagementContent />);
|
||||
|
||||
expect(screen.getByText('All Organizations')).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText('Manage organizations and their members')
|
||||
).toBeInTheDocument();
|
||||
expect(screen.getByText('Manage organizations and their members')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders create organization button', () => {
|
||||
renderWithProviders(<OrganizationManagementContent />);
|
||||
|
||||
expect(
|
||||
screen.getByRole('button', { name: /Create Organization/i })
|
||||
).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: /Create Organization/i })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders OrganizationListTable component', () => {
|
||||
@@ -200,9 +192,7 @@ describe('OrganizationManagementContent', () => {
|
||||
it('does not render dialog initially', () => {
|
||||
renderWithProviders(<OrganizationManagementContent />);
|
||||
|
||||
expect(
|
||||
screen.queryByTestId('organization-form-dialog')
|
||||
).not.toBeInTheDocument();
|
||||
expect(screen.queryByTestId('organization-form-dialog')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -233,9 +223,7 @@ describe('OrganizationManagementContent', () => {
|
||||
await user.click(closeButton);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
screen.queryByTestId('organization-form-dialog')
|
||||
).not.toBeInTheDocument();
|
||||
expect(screen.queryByTestId('organization-form-dialog')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -264,9 +252,7 @@ describe('OrganizationManagementContent', () => {
|
||||
await user.click(closeButton);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
screen.queryByTestId('organization-form-dialog')
|
||||
).not.toBeInTheDocument();
|
||||
expect(screen.queryByTestId('organization-form-dialog')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -21,9 +21,7 @@ jest.mock('sonner', () => ({
|
||||
},
|
||||
}));
|
||||
|
||||
const mockUseBulkUserAction = useBulkUserAction as jest.MockedFunction<
|
||||
typeof useBulkUserAction
|
||||
>;
|
||||
const mockUseBulkUserAction = useBulkUserAction as jest.MockedFunction<typeof useBulkUserAction>;
|
||||
|
||||
describe('BulkActionToolbar', () => {
|
||||
const mockBulkActionMutate = jest.fn();
|
||||
@@ -50,9 +48,7 @@ describe('BulkActionToolbar', () => {
|
||||
/>
|
||||
);
|
||||
|
||||
expect(
|
||||
screen.queryByTestId('bulk-action-toolbar')
|
||||
).not.toBeInTheDocument();
|
||||
expect(screen.queryByTestId('bulk-action-toolbar')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders when one user is selected', () => {
|
||||
@@ -163,9 +159,7 @@ describe('BulkActionToolbar', () => {
|
||||
/>
|
||||
);
|
||||
|
||||
expect(
|
||||
screen.getByRole('button', { name: /Activate/ })
|
||||
).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: /Activate/ })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders deactivate button', () => {
|
||||
@@ -177,9 +171,7 @@ describe('BulkActionToolbar', () => {
|
||||
/>
|
||||
);
|
||||
|
||||
expect(
|
||||
screen.getByRole('button', { name: /Deactivate/ })
|
||||
).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: /Deactivate/ })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders delete button', () => {
|
||||
@@ -191,9 +183,7 @@ describe('BulkActionToolbar', () => {
|
||||
/>
|
||||
);
|
||||
|
||||
expect(
|
||||
screen.getByRole('button', { name: /Delete/ })
|
||||
).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: /Delete/ })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('disables buttons when action is pending', () => {
|
||||
@@ -258,9 +248,7 @@ describe('BulkActionToolbar', () => {
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('Activate Users')).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText(/Are you sure you want to activate 3 users\?/)
|
||||
).toBeInTheDocument();
|
||||
expect(screen.getByText(/Are you sure you want to activate 3 users\?/)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -302,9 +290,7 @@ describe('BulkActionToolbar', () => {
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('Delete Users')).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText(/Are you sure you want to delete 5 users\?/)
|
||||
).toBeInTheDocument();
|
||||
expect(screen.getByText(/Are you sure you want to delete 5 users\?/)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -322,9 +308,7 @@ describe('BulkActionToolbar', () => {
|
||||
await user.click(activateButton);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
screen.getByText(/Are you sure you want to activate 1 user\?/)
|
||||
).toBeInTheDocument();
|
||||
expect(screen.getByText(/Are you sure you want to activate 1 user\?/)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -372,9 +356,7 @@ describe('BulkActionToolbar', () => {
|
||||
/>
|
||||
);
|
||||
|
||||
expect(
|
||||
screen.queryByTestId('bulk-action-toolbar')
|
||||
).not.toBeInTheDocument();
|
||||
expect(screen.queryByTestId('bulk-action-toolbar')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('handles large selection counts', () => {
|
||||
@@ -382,9 +364,7 @@ describe('BulkActionToolbar', () => {
|
||||
<BulkActionToolbar
|
||||
selectedCount={100}
|
||||
onClearSelection={mockOnClearSelection}
|
||||
selectedUserIds={Array.from({ length: 100 }, (_, i) =>
|
||||
String(i + 1)
|
||||
)}
|
||||
selectedUserIds={Array.from({ length: 100 }, (_, i) => String(i + 1))}
|
||||
/>
|
||||
);
|
||||
|
||||
|
||||
@@ -28,12 +28,8 @@ jest.mock('sonner', () => ({
|
||||
},
|
||||
}));
|
||||
|
||||
const mockUseActivateUser = useActivateUser as jest.MockedFunction<
|
||||
typeof useActivateUser
|
||||
>;
|
||||
const mockUseDeactivateUser = useDeactivateUser as jest.MockedFunction<
|
||||
typeof useDeactivateUser
|
||||
>;
|
||||
const mockUseActivateUser = useActivateUser as jest.MockedFunction<typeof useActivateUser>;
|
||||
const mockUseDeactivateUser = useDeactivateUser as jest.MockedFunction<typeof useDeactivateUser>;
|
||||
const mockUseDeleteUser = useDeleteUser as jest.MockedFunction<typeof useDeleteUser>;
|
||||
|
||||
describe('UserActionMenu', () => {
|
||||
@@ -76,13 +72,7 @@ describe('UserActionMenu', () => {
|
||||
|
||||
describe('Menu Rendering', () => {
|
||||
it('renders menu trigger button', () => {
|
||||
render(
|
||||
<UserActionMenu
|
||||
user={mockUser}
|
||||
isCurrentUser={false}
|
||||
onEdit={jest.fn()}
|
||||
/>
|
||||
);
|
||||
render(<UserActionMenu user={mockUser} isCurrentUser={false} onEdit={jest.fn()} />);
|
||||
|
||||
const menuButton = screen.getByRole('button', {
|
||||
name: 'Actions for Test User',
|
||||
@@ -92,13 +82,7 @@ describe('UserActionMenu', () => {
|
||||
|
||||
it('shows menu items when opened', async () => {
|
||||
const user = userEvent.setup();
|
||||
render(
|
||||
<UserActionMenu
|
||||
user={mockUser}
|
||||
isCurrentUser={false}
|
||||
onEdit={jest.fn()}
|
||||
/>
|
||||
);
|
||||
render(<UserActionMenu user={mockUser} isCurrentUser={false} onEdit={jest.fn()} />);
|
||||
|
||||
const menuButton = screen.getByRole('button', {
|
||||
name: 'Actions for Test User',
|
||||
@@ -110,13 +94,7 @@ describe('UserActionMenu', () => {
|
||||
|
||||
it('shows deactivate option for active user', async () => {
|
||||
const user = userEvent.setup();
|
||||
render(
|
||||
<UserActionMenu
|
||||
user={mockUser}
|
||||
isCurrentUser={false}
|
||||
onEdit={jest.fn()}
|
||||
/>
|
||||
);
|
||||
render(<UserActionMenu user={mockUser} isCurrentUser={false} onEdit={jest.fn()} />);
|
||||
|
||||
const menuButton = screen.getByRole('button', {
|
||||
name: 'Actions for Test User',
|
||||
@@ -131,13 +109,7 @@ describe('UserActionMenu', () => {
|
||||
const user = userEvent.setup();
|
||||
const inactiveUser = { ...mockUser, is_active: false };
|
||||
|
||||
render(
|
||||
<UserActionMenu
|
||||
user={inactiveUser}
|
||||
isCurrentUser={false}
|
||||
onEdit={jest.fn()}
|
||||
/>
|
||||
);
|
||||
render(<UserActionMenu user={inactiveUser} isCurrentUser={false} onEdit={jest.fn()} />);
|
||||
|
||||
const menuButton = screen.getByRole('button', {
|
||||
name: 'Actions for Test User',
|
||||
@@ -150,13 +122,7 @@ describe('UserActionMenu', () => {
|
||||
|
||||
it('shows delete option', async () => {
|
||||
const user = userEvent.setup();
|
||||
render(
|
||||
<UserActionMenu
|
||||
user={mockUser}
|
||||
isCurrentUser={false}
|
||||
onEdit={jest.fn()}
|
||||
/>
|
||||
);
|
||||
render(<UserActionMenu user={mockUser} isCurrentUser={false} onEdit={jest.fn()} />);
|
||||
|
||||
const menuButton = screen.getByRole('button', {
|
||||
name: 'Actions for Test User',
|
||||
@@ -172,13 +138,7 @@ describe('UserActionMenu', () => {
|
||||
const user = userEvent.setup();
|
||||
const mockOnEdit = jest.fn();
|
||||
|
||||
render(
|
||||
<UserActionMenu
|
||||
user={mockUser}
|
||||
isCurrentUser={false}
|
||||
onEdit={mockOnEdit}
|
||||
/>
|
||||
);
|
||||
render(<UserActionMenu user={mockUser} isCurrentUser={false} onEdit={mockOnEdit} />);
|
||||
|
||||
const menuButton = screen.getByRole('button', {
|
||||
name: 'Actions for Test User',
|
||||
@@ -195,13 +155,7 @@ describe('UserActionMenu', () => {
|
||||
const user = userEvent.setup();
|
||||
const mockOnEdit = jest.fn();
|
||||
|
||||
render(
|
||||
<UserActionMenu
|
||||
user={mockUser}
|
||||
isCurrentUser={false}
|
||||
onEdit={mockOnEdit}
|
||||
/>
|
||||
);
|
||||
render(<UserActionMenu user={mockUser} isCurrentUser={false} onEdit={mockOnEdit} />);
|
||||
|
||||
const menuButton = screen.getByRole('button', {
|
||||
name: 'Actions for Test User',
|
||||
@@ -222,13 +176,7 @@ describe('UserActionMenu', () => {
|
||||
const user = userEvent.setup();
|
||||
const inactiveUser = { ...mockUser, is_active: false };
|
||||
|
||||
render(
|
||||
<UserActionMenu
|
||||
user={inactiveUser}
|
||||
isCurrentUser={false}
|
||||
onEdit={jest.fn()}
|
||||
/>
|
||||
);
|
||||
render(<UserActionMenu user={inactiveUser} isCurrentUser={false} onEdit={jest.fn()} />);
|
||||
|
||||
const menuButton = screen.getByRole('button', {
|
||||
name: 'Actions for Test User',
|
||||
@@ -247,13 +195,7 @@ describe('UserActionMenu', () => {
|
||||
const user = userEvent.setup();
|
||||
const inactiveUser = { ...mockUser, is_active: false };
|
||||
|
||||
render(
|
||||
<UserActionMenu
|
||||
user={inactiveUser}
|
||||
isCurrentUser={false}
|
||||
onEdit={jest.fn()}
|
||||
/>
|
||||
);
|
||||
render(<UserActionMenu user={inactiveUser} isCurrentUser={false} onEdit={jest.fn()} />);
|
||||
|
||||
const menuButton = screen.getByRole('button', {
|
||||
name: 'Actions for Test User',
|
||||
@@ -264,9 +206,7 @@ describe('UserActionMenu', () => {
|
||||
await user.click(activateButton);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(toast.success).toHaveBeenCalledWith(
|
||||
'Test User has been activated successfully.'
|
||||
);
|
||||
expect(toast.success).toHaveBeenCalledWith('Test User has been activated successfully.');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -276,13 +216,7 @@ describe('UserActionMenu', () => {
|
||||
|
||||
mockActivateMutate.mockRejectedValueOnce(new Error('Network error'));
|
||||
|
||||
render(
|
||||
<UserActionMenu
|
||||
user={inactiveUser}
|
||||
isCurrentUser={false}
|
||||
onEdit={jest.fn()}
|
||||
/>
|
||||
);
|
||||
render(<UserActionMenu user={inactiveUser} isCurrentUser={false} onEdit={jest.fn()} />);
|
||||
|
||||
const menuButton = screen.getByRole('button', {
|
||||
name: 'Actions for Test User',
|
||||
@@ -302,13 +236,7 @@ describe('UserActionMenu', () => {
|
||||
it('shows confirmation dialog when deactivate is clicked', async () => {
|
||||
const user = userEvent.setup();
|
||||
|
||||
render(
|
||||
<UserActionMenu
|
||||
user={mockUser}
|
||||
isCurrentUser={false}
|
||||
onEdit={jest.fn()}
|
||||
/>
|
||||
);
|
||||
render(<UserActionMenu user={mockUser} isCurrentUser={false} onEdit={jest.fn()} />);
|
||||
|
||||
const menuButton = screen.getByRole('button', {
|
||||
name: 'Actions for Test User',
|
||||
@@ -320,22 +248,14 @@ describe('UserActionMenu', () => {
|
||||
|
||||
expect(screen.getByText('Deactivate User')).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText(
|
||||
/Are you sure you want to deactivate Test User\?/
|
||||
)
|
||||
screen.getByText(/Are you sure you want to deactivate Test User\?/)
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows confirmation dialog when deactivate is clicked', async () => {
|
||||
const user = userEvent.setup();
|
||||
|
||||
render(
|
||||
<UserActionMenu
|
||||
user={mockUser}
|
||||
isCurrentUser={false}
|
||||
onEdit={jest.fn()}
|
||||
/>
|
||||
);
|
||||
render(<UserActionMenu user={mockUser} isCurrentUser={false} onEdit={jest.fn()} />);
|
||||
|
||||
const menuButton = screen.getByRole('button', {
|
||||
name: 'Actions for Test User',
|
||||
@@ -357,13 +277,7 @@ describe('UserActionMenu', () => {
|
||||
it('disables deactivate option for current user', async () => {
|
||||
const user = userEvent.setup();
|
||||
|
||||
render(
|
||||
<UserActionMenu
|
||||
user={mockUser}
|
||||
isCurrentUser={true}
|
||||
onEdit={jest.fn()}
|
||||
/>
|
||||
);
|
||||
render(<UserActionMenu user={mockUser} isCurrentUser={true} onEdit={jest.fn()} />);
|
||||
|
||||
const menuButton = screen.getByRole('button', {
|
||||
name: 'Actions for Test User',
|
||||
@@ -380,13 +294,7 @@ describe('UserActionMenu', () => {
|
||||
it('shows confirmation dialog when delete is clicked', async () => {
|
||||
const user = userEvent.setup();
|
||||
|
||||
render(
|
||||
<UserActionMenu
|
||||
user={mockUser}
|
||||
isCurrentUser={false}
|
||||
onEdit={jest.fn()}
|
||||
/>
|
||||
);
|
||||
render(<UserActionMenu user={mockUser} isCurrentUser={false} onEdit={jest.fn()} />);
|
||||
|
||||
const menuButton = screen.getByRole('button', {
|
||||
name: 'Actions for Test User',
|
||||
@@ -397,24 +305,14 @@ describe('UserActionMenu', () => {
|
||||
await user.click(deleteButton);
|
||||
|
||||
expect(screen.getByText('Delete User')).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText(/Are you sure you want to delete Test User\?/)
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText(/This action cannot be undone\./)
|
||||
).toBeInTheDocument();
|
||||
expect(screen.getByText(/Are you sure you want to delete Test User\?/)).toBeInTheDocument();
|
||||
expect(screen.getByText(/This action cannot be undone\./)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('deletes user when confirmed', async () => {
|
||||
const user = userEvent.setup();
|
||||
|
||||
render(
|
||||
<UserActionMenu
|
||||
user={mockUser}
|
||||
isCurrentUser={false}
|
||||
onEdit={jest.fn()}
|
||||
/>
|
||||
);
|
||||
render(<UserActionMenu user={mockUser} isCurrentUser={false} onEdit={jest.fn()} />);
|
||||
|
||||
const menuButton = screen.getByRole('button', {
|
||||
name: 'Actions for Test User',
|
||||
@@ -435,13 +333,7 @@ describe('UserActionMenu', () => {
|
||||
it('cancels deletion when cancel is clicked', async () => {
|
||||
const user = userEvent.setup();
|
||||
|
||||
render(
|
||||
<UserActionMenu
|
||||
user={mockUser}
|
||||
isCurrentUser={false}
|
||||
onEdit={jest.fn()}
|
||||
/>
|
||||
);
|
||||
render(<UserActionMenu user={mockUser} isCurrentUser={false} onEdit={jest.fn()} />);
|
||||
|
||||
const menuButton = screen.getByRole('button', {
|
||||
name: 'Actions for Test User',
|
||||
@@ -460,13 +352,7 @@ describe('UserActionMenu', () => {
|
||||
it('shows success toast on deletion', async () => {
|
||||
const user = userEvent.setup();
|
||||
|
||||
render(
|
||||
<UserActionMenu
|
||||
user={mockUser}
|
||||
isCurrentUser={false}
|
||||
onEdit={jest.fn()}
|
||||
/>
|
||||
);
|
||||
render(<UserActionMenu user={mockUser} isCurrentUser={false} onEdit={jest.fn()} />);
|
||||
|
||||
const menuButton = screen.getByRole('button', {
|
||||
name: 'Actions for Test User',
|
||||
@@ -480,22 +366,14 @@ describe('UserActionMenu', () => {
|
||||
await user.click(confirmButton);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(toast.success).toHaveBeenCalledWith(
|
||||
'Test User has been deleted successfully.'
|
||||
);
|
||||
expect(toast.success).toHaveBeenCalledWith('Test User has been deleted successfully.');
|
||||
});
|
||||
});
|
||||
|
||||
it('disables delete option for current user', async () => {
|
||||
const user = userEvent.setup();
|
||||
|
||||
render(
|
||||
<UserActionMenu
|
||||
user={mockUser}
|
||||
isCurrentUser={true}
|
||||
onEdit={jest.fn()}
|
||||
/>
|
||||
);
|
||||
render(<UserActionMenu user={mockUser} isCurrentUser={true} onEdit={jest.fn()} />);
|
||||
|
||||
const menuButton = screen.getByRole('button', {
|
||||
name: 'Actions for Test User',
|
||||
@@ -510,13 +388,7 @@ describe('UserActionMenu', () => {
|
||||
|
||||
describe('User Name Display', () => {
|
||||
it('displays full name when last name is provided', async () => {
|
||||
render(
|
||||
<UserActionMenu
|
||||
user={mockUser}
|
||||
isCurrentUser={false}
|
||||
onEdit={jest.fn()}
|
||||
/>
|
||||
);
|
||||
render(<UserActionMenu user={mockUser} isCurrentUser={false} onEdit={jest.fn()} />);
|
||||
|
||||
const menuButton = screen.getByRole('button', {
|
||||
name: 'Actions for Test User',
|
||||
@@ -528,11 +400,7 @@ describe('UserActionMenu', () => {
|
||||
const userWithoutLastName = { ...mockUser, last_name: null };
|
||||
|
||||
render(
|
||||
<UserActionMenu
|
||||
user={userWithoutLastName}
|
||||
isCurrentUser={false}
|
||||
onEdit={jest.fn()}
|
||||
/>
|
||||
<UserActionMenu user={userWithoutLastName} isCurrentUser={false} onEdit={jest.fn()} />
|
||||
);
|
||||
|
||||
const menuButton = screen.getByRole('button', {
|
||||
@@ -549,13 +417,7 @@ describe('UserActionMenu', () => {
|
||||
|
||||
mockActivateMutate.mockRejectedValueOnce(new Error('Custom error'));
|
||||
|
||||
render(
|
||||
<UserActionMenu
|
||||
user={inactiveUser}
|
||||
isCurrentUser={false}
|
||||
onEdit={jest.fn()}
|
||||
/>
|
||||
);
|
||||
render(<UserActionMenu user={inactiveUser} isCurrentUser={false} onEdit={jest.fn()} />);
|
||||
|
||||
const menuButton = screen.getByRole('button', {
|
||||
name: 'Actions for Test User',
|
||||
@@ -576,13 +438,7 @@ describe('UserActionMenu', () => {
|
||||
|
||||
mockActivateMutate.mockRejectedValueOnce('String error');
|
||||
|
||||
render(
|
||||
<UserActionMenu
|
||||
user={inactiveUser}
|
||||
isCurrentUser={false}
|
||||
onEdit={jest.fn()}
|
||||
/>
|
||||
);
|
||||
render(<UserActionMenu user={inactiveUser} isCurrentUser={false} onEdit={jest.fn()} />);
|
||||
|
||||
const menuButton = screen.getByRole('button', {
|
||||
name: 'Actions for Test User',
|
||||
|
||||
@@ -11,9 +11,7 @@ import type { User, PaginationMeta } from '@/lib/api/hooks/useAdmin';
|
||||
// Mock UserActionMenu component
|
||||
jest.mock('@/components/admin/users/UserActionMenu', () => ({
|
||||
UserActionMenu: ({ user, isCurrentUser }: any) => (
|
||||
<button data-testid={`action-menu-${user.id}`}>
|
||||
Actions {isCurrentUser && '(current)'}
|
||||
</button>
|
||||
<button data-testid={`action-menu-${user.id}`}>Actions {isCurrentUser && '(current)'}</button>
|
||||
),
|
||||
}));
|
||||
|
||||
@@ -139,25 +137,15 @@ describe('UserListTable', () => {
|
||||
describe('Empty State', () => {
|
||||
it('shows empty message when no users', () => {
|
||||
render(
|
||||
<UserListTable
|
||||
{...defaultProps}
|
||||
users={[]}
|
||||
pagination={{ ...mockPagination, total: 0 }}
|
||||
/>
|
||||
<UserListTable {...defaultProps} users={[]} pagination={{ ...mockPagination, total: 0 }} />
|
||||
);
|
||||
|
||||
expect(
|
||||
screen.getByText('No users found. Try adjusting your filters.')
|
||||
).toBeInTheDocument();
|
||||
expect(screen.getByText('No users found. Try adjusting your filters.')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('does not render pagination when no users', () => {
|
||||
render(
|
||||
<UserListTable
|
||||
{...defaultProps}
|
||||
users={[]}
|
||||
pagination={{ ...mockPagination, total: 0 }}
|
||||
/>
|
||||
<UserListTable {...defaultProps} users={[]} pagination={{ ...mockPagination, total: 0 }} />
|
||||
);
|
||||
|
||||
expect(screen.queryByText(/Showing/)).not.toBeInTheDocument();
|
||||
@@ -168,9 +156,7 @@ describe('UserListTable', () => {
|
||||
it('renders search input', () => {
|
||||
render(<UserListTable {...defaultProps} />);
|
||||
|
||||
const searchInput = screen.getByPlaceholderText(
|
||||
'Search by name or email...'
|
||||
);
|
||||
const searchInput = screen.getByPlaceholderText('Search by name or email...');
|
||||
expect(searchInput).toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -178,9 +164,7 @@ describe('UserListTable', () => {
|
||||
const user = userEvent.setup();
|
||||
render(<UserListTable {...defaultProps} />);
|
||||
|
||||
const searchInput = screen.getByPlaceholderText(
|
||||
'Search by name or email...'
|
||||
);
|
||||
const searchInput = screen.getByPlaceholderText('Search by name or email...');
|
||||
|
||||
await user.type(searchInput, 'alice');
|
||||
|
||||
@@ -222,8 +206,8 @@ describe('UserListTable', () => {
|
||||
|
||||
// Find "All Users" in the filter dropdown (not the heading)
|
||||
const selectTriggers = screen.getAllByRole('combobox');
|
||||
const userTypeFilter = selectTriggers.find(trigger =>
|
||||
within(trigger).queryByText('All Users') !== null
|
||||
const userTypeFilter = selectTriggers.find(
|
||||
(trigger) => within(trigger).queryByText('All Users') !== null
|
||||
);
|
||||
|
||||
expect(userTypeFilter).toBeInTheDocument();
|
||||
@@ -298,11 +282,7 @@ describe('UserListTable', () => {
|
||||
|
||||
it('disables select all checkbox when no users', () => {
|
||||
render(
|
||||
<UserListTable
|
||||
{...defaultProps}
|
||||
users={[]}
|
||||
pagination={{ ...mockPagination, total: 0 }}
|
||||
/>
|
||||
<UserListTable {...defaultProps} users={[]} pagination={{ ...mockPagination, total: 0 }} />
|
||||
);
|
||||
|
||||
const selectAllCheckbox = screen.getByLabelText('Select all users');
|
||||
@@ -433,11 +413,7 @@ describe('UserListTable', () => {
|
||||
|
||||
it('does not render pagination when no users', () => {
|
||||
render(
|
||||
<UserListTable
|
||||
{...defaultProps}
|
||||
users={[]}
|
||||
pagination={{ ...mockPagination, total: 0 }}
|
||||
/>
|
||||
<UserListTable {...defaultProps} users={[]} pagination={{ ...mockPagination, total: 0 }} />
|
||||
);
|
||||
|
||||
expect(screen.queryByText(/Showing/)).not.toBeInTheDocument();
|
||||
|
||||
@@ -36,9 +36,7 @@ jest.mock('@/lib/api/hooks/useAdmin', () => ({
|
||||
jest.mock('@/components/admin/users/UserListTable', () => ({
|
||||
UserListTable: ({ onEditUser, onSelectUser, selectedUsers }: any) => (
|
||||
<div data-testid="user-list-table">
|
||||
<button onClick={() => onEditUser({ id: '1', first_name: 'Test' })}>
|
||||
Edit User
|
||||
</button>
|
||||
<button onClick={() => onEditUser({ id: '1', first_name: 'Test' })}>Edit User</button>
|
||||
<button onClick={() => onSelectUser('1')}>Select User 1</button>
|
||||
<div data-testid="selected-count">{selectedUsers.length}</div>
|
||||
</div>
|
||||
@@ -67,13 +65,9 @@ jest.mock('@/components/admin/users/BulkActionToolbar', () => ({
|
||||
}));
|
||||
|
||||
const mockUseRouter = useRouter as jest.MockedFunction<typeof useRouter>;
|
||||
const mockUseSearchParams = useSearchParams as jest.MockedFunction<
|
||||
typeof useSearchParams
|
||||
>;
|
||||
const mockUseSearchParams = useSearchParams as jest.MockedFunction<typeof useSearchParams>;
|
||||
const mockUseAuth = useAuth as jest.MockedFunction<typeof useAuth>;
|
||||
const mockUseAdminUsers = useAdminUsers as jest.MockedFunction<
|
||||
typeof useAdminUsers
|
||||
>;
|
||||
const mockUseAdminUsers = useAdminUsers as jest.MockedFunction<typeof useAdminUsers>;
|
||||
|
||||
// Import mutation hooks for mocking
|
||||
const {
|
||||
@@ -207,9 +201,7 @@ describe('UserManagementContent', () => {
|
||||
});
|
||||
|
||||
const renderWithProviders = (ui: React.ReactElement) => {
|
||||
return render(
|
||||
<QueryClientProvider client={queryClient}>{ui}</QueryClientProvider>
|
||||
);
|
||||
return render(<QueryClientProvider client={queryClient}>{ui}</QueryClientProvider>);
|
||||
};
|
||||
|
||||
describe('Component Rendering', () => {
|
||||
@@ -217,17 +209,13 @@ describe('UserManagementContent', () => {
|
||||
renderWithProviders(<UserManagementContent />);
|
||||
|
||||
expect(screen.getByText('All Users')).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText('Manage user accounts and permissions')
|
||||
).toBeInTheDocument();
|
||||
expect(screen.getByText('Manage user accounts and permissions')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders create user button', () => {
|
||||
renderWithProviders(<UserManagementContent />);
|
||||
|
||||
expect(
|
||||
screen.getByRole('button', { name: /Create User/i })
|
||||
).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: /Create User/i })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders UserListTable component', () => {
|
||||
@@ -239,17 +227,13 @@ describe('UserManagementContent', () => {
|
||||
it('does not render dialog initially', () => {
|
||||
renderWithProviders(<UserManagementContent />);
|
||||
|
||||
expect(
|
||||
screen.queryByTestId('user-form-dialog')
|
||||
).not.toBeInTheDocument();
|
||||
expect(screen.queryByTestId('user-form-dialog')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('does not render bulk toolbar initially', () => {
|
||||
renderWithProviders(<UserManagementContent />);
|
||||
|
||||
expect(
|
||||
screen.queryByTestId('bulk-action-toolbar')
|
||||
).not.toBeInTheDocument();
|
||||
expect(screen.queryByTestId('bulk-action-toolbar')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -280,9 +264,7 @@ describe('UserManagementContent', () => {
|
||||
await user.click(closeButton);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
screen.queryByTestId('user-form-dialog')
|
||||
).not.toBeInTheDocument();
|
||||
expect(screen.queryByTestId('user-form-dialog')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -311,9 +293,7 @@ describe('UserManagementContent', () => {
|
||||
await user.click(closeButton);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
screen.queryByTestId('user-form-dialog')
|
||||
).not.toBeInTheDocument();
|
||||
expect(screen.queryByTestId('user-form-dialog')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -338,9 +318,7 @@ describe('UserManagementContent', () => {
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('bulk-action-toolbar')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('bulk-selected-count')).toHaveTextContent(
|
||||
'1'
|
||||
);
|
||||
expect(screen.getByTestId('bulk-selected-count')).toHaveTextContent('1');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -362,9 +340,7 @@ describe('UserManagementContent', () => {
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('selected-count')).toHaveTextContent('0');
|
||||
expect(
|
||||
screen.queryByTestId('bulk-action-toolbar')
|
||||
).not.toBeInTheDocument();
|
||||
expect(screen.queryByTestId('bulk-action-toolbar')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -521,9 +497,7 @@ describe('UserManagementContent', () => {
|
||||
await user.click(selectButton);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('bulk-selected-count')).toHaveTextContent(
|
||||
'1'
|
||||
);
|
||||
expect(screen.getByTestId('bulk-selected-count')).toHaveTextContent('1');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user