add tests
This commit is contained in:
361
apps/website/components/admin/AdminUsersTable.test.tsx
Normal file
361
apps/website/components/admin/AdminUsersTable.test.tsx
Normal file
@@ -0,0 +1,361 @@
|
||||
/**
|
||||
* AdminUsersTable Component Tests
|
||||
*
|
||||
* Tests for the AdminUsersTable component that displays users in a table
|
||||
* with selection, status management, and deletion capabilities.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { render, screen, fireEvent } from '@testing-library/react';
|
||||
import { AdminUsersTable } from './AdminUsersTable';
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
|
||||
// Mock the DateDisplay component
|
||||
vi.mock('@/lib/display-objects/DateDisplay', () => ({
|
||||
DateDisplay: {
|
||||
formatShort: (date: string) => new Date(date).toLocaleDateString(),
|
||||
},
|
||||
}));
|
||||
|
||||
// Mock the AdminUsersViewData
|
||||
vi.mock('@/lib/view-data/AdminUsersViewData', () => ({
|
||||
AdminUsersViewData: {},
|
||||
}));
|
||||
|
||||
// Mock the Button component
|
||||
vi.mock('@/ui/Button', () => ({
|
||||
Button: ({ children, onClick, disabled }: any) => (
|
||||
<button onClick={onClick} disabled={disabled} data-testid="button">
|
||||
{children}
|
||||
</button>
|
||||
),
|
||||
}));
|
||||
|
||||
// Mock the IconButton component
|
||||
vi.mock('@/ui/IconButton', () => ({
|
||||
IconButton: ({ onClick, disabled, icon, title }: any) => (
|
||||
<button onClick={onClick} disabled={disabled} data-testid="icon-button" title={title}>
|
||||
{title}
|
||||
</button>
|
||||
),
|
||||
}));
|
||||
|
||||
// Mock the SimpleCheckbox component
|
||||
vi.mock('@/ui/SimpleCheckbox', () => ({
|
||||
SimpleCheckbox: ({ checked, onChange, 'aria-label': ariaLabel }: any) => (
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={checked}
|
||||
onChange={onChange}
|
||||
aria-label={ariaLabel}
|
||||
data-testid="checkbox"
|
||||
/>
|
||||
),
|
||||
}));
|
||||
|
||||
// Mock the Badge component
|
||||
vi.mock('@/ui/Badge', () => ({
|
||||
Badge: ({ children }: any) => <span data-testid="badge">{children}</span>,
|
||||
}));
|
||||
|
||||
// Mock the Box component
|
||||
vi.mock('@/ui/Box', () => ({
|
||||
Box: ({ children }: any) => <div>{children}</div>,
|
||||
}));
|
||||
|
||||
// Mock the Group component
|
||||
vi.mock('@/ui/Group', () => ({
|
||||
Group: ({ children }: any) => <div>{children}</div>,
|
||||
}));
|
||||
|
||||
// Mock the DriverIdentity component
|
||||
vi.mock('@/ui/DriverIdentity', () => ({
|
||||
DriverIdentity: ({ driver, meta }: any) => (
|
||||
<div data-testid="driver-identity">
|
||||
<span>{driver.name}</span>
|
||||
<span>{meta}</span>
|
||||
</div>
|
||||
),
|
||||
}));
|
||||
|
||||
// Mock the Table components
|
||||
vi.mock('@/ui/Table', () => ({
|
||||
Table: ({ children }: any) => <table>{children}</table>,
|
||||
TableHead: ({ children }: any) => <thead>{children}</thead>,
|
||||
TableBody: ({ children }: any) => <tbody>{children}</tbody>,
|
||||
TableHeader: ({ children, w, textAlign }: any) => <th style={{ width: w, textAlign }}>{children}</th>,
|
||||
TableRow: ({ children, variant }: any) => <tr data-variant={variant}>{children}</tr>,
|
||||
TableCell: ({ children }: any) => <td>{children}</td>,
|
||||
}));
|
||||
|
||||
// Mock the Text component
|
||||
vi.mock('@/ui/Text', () => ({
|
||||
Text: ({ children, size, variant }: any) => (
|
||||
<span data-size={size} data-variant={variant}>{children}</span>
|
||||
),
|
||||
}));
|
||||
|
||||
// Mock the UserStatusTag component
|
||||
vi.mock('./UserStatusTag', () => ({
|
||||
UserStatusTag: ({ status }: any) => <span data-testid="status-tag">{status}</span>,
|
||||
}));
|
||||
|
||||
describe('AdminUsersTable', () => {
|
||||
const mockUsers = [
|
||||
{
|
||||
id: '1',
|
||||
displayName: 'John Doe',
|
||||
email: 'john@example.com',
|
||||
roles: ['admin'],
|
||||
status: 'active',
|
||||
lastLoginAt: '2024-01-15T10:30:00Z',
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
displayName: 'Jane Smith',
|
||||
email: 'jane@example.com',
|
||||
roles: ['user'],
|
||||
status: 'suspended',
|
||||
lastLoginAt: '2024-01-14T15:45:00Z',
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
displayName: 'Bob Johnson',
|
||||
email: 'bob@example.com',
|
||||
roles: ['user'],
|
||||
status: 'active',
|
||||
lastLoginAt: null,
|
||||
},
|
||||
];
|
||||
|
||||
const defaultProps = {
|
||||
users: mockUsers,
|
||||
selectedUserIds: [],
|
||||
onSelectUser: vi.fn(),
|
||||
onSelectAll: vi.fn(),
|
||||
onUpdateStatus: vi.fn(),
|
||||
onDeleteUser: vi.fn(),
|
||||
deletingUserId: null,
|
||||
};
|
||||
|
||||
it('should render table headers', () => {
|
||||
render(<AdminUsersTable {...defaultProps} />);
|
||||
|
||||
expect(screen.getByText('User')).toBeTruthy();
|
||||
expect(screen.getByText('Roles')).toBeTruthy();
|
||||
expect(screen.getByText('Status')).toBeTruthy();
|
||||
expect(screen.getByText('Last Login')).toBeTruthy();
|
||||
expect(screen.getByText('Actions')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should render user rows', () => {
|
||||
render(<AdminUsersTable {...defaultProps} />);
|
||||
|
||||
expect(screen.getByText('John Doe')).toBeTruthy();
|
||||
expect(screen.getByText('john@example.com')).toBeTruthy();
|
||||
expect(screen.getByText('Jane Smith')).toBeTruthy();
|
||||
expect(screen.getByText('jane@example.com')).toBeTruthy();
|
||||
expect(screen.getByText('Bob Johnson')).toBeTruthy();
|
||||
expect(screen.getByText('bob@example.com')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should render user roles', () => {
|
||||
render(<AdminUsersTable {...defaultProps} />);
|
||||
|
||||
expect(screen.getByText('admin')).toBeTruthy();
|
||||
expect(screen.getByText('user')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should render user status tags', () => {
|
||||
render(<AdminUsersTable {...defaultProps} />);
|
||||
|
||||
expect(screen.getAllByTestId('status-tag')).toHaveLength(3);
|
||||
});
|
||||
|
||||
it('should render last login dates', () => {
|
||||
render(<AdminUsersTable {...defaultProps} />);
|
||||
|
||||
expect(screen.getByText('1/15/2024')).toBeTruthy();
|
||||
expect(screen.getByText('1/14/2024')).toBeTruthy();
|
||||
expect(screen.getByText('Never')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should render select all checkbox', () => {
|
||||
render(<AdminUsersTable {...defaultProps} />);
|
||||
|
||||
expect(screen.getByLabelText('Select all users')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should render individual user checkboxes', () => {
|
||||
render(<AdminUsersTable {...defaultProps} />);
|
||||
|
||||
expect(screen.getByLabelText('Select user John Doe')).toBeTruthy();
|
||||
expect(screen.getByLabelText('Select user Jane Smith')).toBeTruthy();
|
||||
expect(screen.getByLabelText('Select user Bob Johnson')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should render suspend button for active users', () => {
|
||||
render(<AdminUsersTable {...defaultProps} />);
|
||||
|
||||
expect(screen.getByText('Suspend')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should render activate button for suspended users', () => {
|
||||
render(<AdminUsersTable {...defaultProps} />);
|
||||
|
||||
expect(screen.getByText('Activate')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should render delete button for all users', () => {
|
||||
render(<AdminUsersTable {...defaultProps} />);
|
||||
|
||||
expect(screen.getAllByTitle('Delete')).toHaveLength(3);
|
||||
});
|
||||
|
||||
it('should render more button for all users', () => {
|
||||
render(<AdminUsersTable {...defaultProps} />);
|
||||
|
||||
expect(screen.getAllByTitle('More')).toHaveLength(3);
|
||||
});
|
||||
|
||||
it('should highlight selected rows', () => {
|
||||
const props = {
|
||||
...defaultProps,
|
||||
selectedUserIds: ['1', '3'],
|
||||
};
|
||||
|
||||
render(<AdminUsersTable {...props} />);
|
||||
|
||||
// Check that selected rows have highlight variant
|
||||
const rows = screen.getAllByRole('row');
|
||||
expect(rows[1]).toHaveAttribute('data-variant', 'highlight');
|
||||
expect(rows[3]).toHaveAttribute('data-variant', 'highlight');
|
||||
});
|
||||
|
||||
it('should disable delete button when deleting', () => {
|
||||
const props = {
|
||||
...defaultProps,
|
||||
deletingUserId: '1',
|
||||
};
|
||||
|
||||
render(<AdminUsersTable {...props} />);
|
||||
|
||||
const deleteButtons = screen.getAllByTitle('Delete');
|
||||
expect(deleteButtons[0]).toBeDisabled();
|
||||
});
|
||||
|
||||
it('should call onSelectUser when checkbox is clicked', () => {
|
||||
const onSelectUser = vi.fn();
|
||||
const props = {
|
||||
...defaultProps,
|
||||
onSelectUser,
|
||||
};
|
||||
|
||||
render(<AdminUsersTable {...props} />);
|
||||
|
||||
const checkboxes = screen.getAllByTestId('checkbox');
|
||||
fireEvent.click(checkboxes[1]); // Click first user checkbox
|
||||
|
||||
expect(onSelectUser).toHaveBeenCalledWith('1');
|
||||
});
|
||||
|
||||
it('should call onSelectAll when select all checkbox is clicked', () => {
|
||||
const onSelectAll = vi.fn();
|
||||
const props = {
|
||||
...defaultProps,
|
||||
onSelectAll,
|
||||
};
|
||||
|
||||
render(<AdminUsersTable {...props} />);
|
||||
|
||||
const selectAllCheckbox = screen.getByLabelText('Select all users');
|
||||
fireEvent.click(selectAllCheckbox);
|
||||
|
||||
expect(onSelectAll).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call onUpdateStatus when suspend button is clicked', () => {
|
||||
const onUpdateStatus = vi.fn();
|
||||
const props = {
|
||||
...defaultProps,
|
||||
onUpdateStatus,
|
||||
};
|
||||
|
||||
render(<AdminUsersTable {...props} />);
|
||||
|
||||
const suspendButtons = screen.getAllByText('Suspend');
|
||||
fireEvent.click(suspendButtons[0]);
|
||||
|
||||
expect(onUpdateStatus).toHaveBeenCalledWith('1', 'suspended');
|
||||
});
|
||||
|
||||
it('should call onUpdateStatus when activate button is clicked', () => {
|
||||
const onUpdateStatus = vi.fn();
|
||||
const props = {
|
||||
...defaultProps,
|
||||
onUpdateStatus,
|
||||
};
|
||||
|
||||
render(<AdminUsersTable {...props} />);
|
||||
|
||||
const activateButtons = screen.getAllByText('Activate');
|
||||
fireEvent.click(activateButtons[0]);
|
||||
|
||||
expect(onUpdateStatus).toHaveBeenCalledWith('2', 'active');
|
||||
});
|
||||
|
||||
it('should call onDeleteUser when delete button is clicked', () => {
|
||||
const onDeleteUser = vi.fn();
|
||||
const props = {
|
||||
...defaultProps,
|
||||
onDeleteUser,
|
||||
};
|
||||
|
||||
render(<AdminUsersTable {...props} />);
|
||||
|
||||
const deleteButtons = screen.getAllByTitle('Delete');
|
||||
fireEvent.click(deleteButtons[0]);
|
||||
|
||||
expect(onDeleteUser).toHaveBeenCalledWith('1');
|
||||
});
|
||||
|
||||
it('should render empty table when no users', () => {
|
||||
const props = {
|
||||
...defaultProps,
|
||||
users: [],
|
||||
};
|
||||
|
||||
render(<AdminUsersTable {...props} />);
|
||||
|
||||
// Should render table headers but no rows
|
||||
expect(screen.getByText('User')).toBeTruthy();
|
||||
expect(screen.getByText('Roles')).toBeTruthy();
|
||||
expect(screen.getByText('Status')).toBeTruthy();
|
||||
expect(screen.getByText('Last Login')).toBeTruthy();
|
||||
expect(screen.getByText('Actions')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should render with all users selected', () => {
|
||||
const props = {
|
||||
...defaultProps,
|
||||
selectedUserIds: ['1', '2', '3'],
|
||||
};
|
||||
|
||||
render(<AdminUsersTable {...props} />);
|
||||
|
||||
const selectAllCheckbox = screen.getByLabelText('Select all users');
|
||||
expect(selectAllCheckbox).toBeChecked();
|
||||
});
|
||||
|
||||
it('should render with some users selected', () => {
|
||||
const props = {
|
||||
...defaultProps,
|
||||
selectedUserIds: ['1', '2'],
|
||||
};
|
||||
|
||||
render(<AdminUsersTable {...props} />);
|
||||
|
||||
const selectAllCheckbox = screen.getByLabelText('Select all users');
|
||||
expect(selectAllCheckbox).not.toBeChecked();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user