- Add frontend unit tests with Vitest for components, hooks, pages, and utilities - Add backend tests for webhooks, notifications, middleware, and edge cases - Add ForgotPassword, NotFound, and ResetPassword pages - Add migration for orphaned staff resources conversion - Add coverage directory to gitignore (generated reports) - Various bug fixes and improvements from previous work 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
715 lines
22 KiB
TypeScript
715 lines
22 KiB
TypeScript
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
import { render, screen } from '@testing-library/react';
|
|
import userEvent from '@testing-library/user-event';
|
|
import { BrowserRouter, MemoryRouter } from 'react-router-dom';
|
|
import PlatformSidebar from '../PlatformSidebar';
|
|
import { User } from '../../types';
|
|
|
|
// Mock the i18next module
|
|
vi.mock('react-i18next', () => ({
|
|
useTranslation: () => ({
|
|
t: (key: string, fallback?: string) => {
|
|
const translations: Record<string, string> = {
|
|
'nav.platformDashboard': 'Platform Dashboard',
|
|
'nav.dashboard': 'Dashboard',
|
|
'nav.businesses': 'Businesses',
|
|
'nav.users': 'Users',
|
|
'nav.support': 'Support',
|
|
'nav.staff': 'Staff',
|
|
'nav.platformSettings': 'Platform Settings',
|
|
'nav.help': 'Help',
|
|
'nav.apiDocs': 'API Docs',
|
|
};
|
|
return translations[key] || fallback || key;
|
|
},
|
|
}),
|
|
}));
|
|
|
|
// Mock the SmoothScheduleLogo component
|
|
vi.mock('../SmoothScheduleLogo', () => ({
|
|
default: ({ className }: { className?: string }) => (
|
|
<div data-testid="smooth-schedule-logo" className={className}>Logo</div>
|
|
),
|
|
}));
|
|
|
|
describe('PlatformSidebar', () => {
|
|
const mockSuperuser: User = {
|
|
id: '1',
|
|
name: 'Super User',
|
|
email: 'super@example.com',
|
|
role: 'superuser',
|
|
};
|
|
|
|
const mockPlatformManager: User = {
|
|
id: '2',
|
|
name: 'Platform Manager',
|
|
email: 'manager@example.com',
|
|
role: 'platform_manager',
|
|
};
|
|
|
|
const mockPlatformSupport: User = {
|
|
id: '3',
|
|
name: 'Platform Support',
|
|
email: 'support@example.com',
|
|
role: 'platform_support',
|
|
};
|
|
|
|
const mockToggleCollapse = vi.fn();
|
|
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
});
|
|
|
|
describe('Rendering', () => {
|
|
it('renders the sidebar with logo and user role', () => {
|
|
render(
|
|
<BrowserRouter>
|
|
<PlatformSidebar
|
|
user={mockSuperuser}
|
|
isCollapsed={false}
|
|
toggleCollapse={mockToggleCollapse}
|
|
/>
|
|
</BrowserRouter>
|
|
);
|
|
|
|
expect(screen.getByTestId('smooth-schedule-logo')).toBeInTheDocument();
|
|
expect(screen.getByText('Smooth Schedule')).toBeInTheDocument();
|
|
expect(screen.getByText('superuser')).toBeInTheDocument();
|
|
});
|
|
|
|
it('renders all navigation links for superuser', () => {
|
|
render(
|
|
<BrowserRouter>
|
|
<PlatformSidebar
|
|
user={mockSuperuser}
|
|
isCollapsed={false}
|
|
toggleCollapse={mockToggleCollapse}
|
|
/>
|
|
</BrowserRouter>
|
|
);
|
|
|
|
// Operations section
|
|
expect(screen.getByText('Operations')).toBeInTheDocument();
|
|
expect(screen.getByText('Dashboard')).toBeInTheDocument();
|
|
expect(screen.getByText('Businesses')).toBeInTheDocument();
|
|
expect(screen.getByText('Users')).toBeInTheDocument();
|
|
expect(screen.getByText('Support')).toBeInTheDocument();
|
|
expect(screen.getAllByText('Email Addresses')[0]).toBeInTheDocument();
|
|
|
|
// System section (superuser only)
|
|
expect(screen.getByText('System')).toBeInTheDocument();
|
|
expect(screen.getByText('Staff')).toBeInTheDocument();
|
|
expect(screen.getByText('Platform Settings')).toBeInTheDocument();
|
|
|
|
// Help section
|
|
expect(screen.getByText('Help')).toBeInTheDocument();
|
|
expect(screen.getAllByText('Email Settings')[0]).toBeInTheDocument();
|
|
expect(screen.getByText('API Docs')).toBeInTheDocument();
|
|
});
|
|
|
|
it('hides system section for platform manager', () => {
|
|
render(
|
|
<BrowserRouter>
|
|
<PlatformSidebar
|
|
user={mockPlatformManager}
|
|
isCollapsed={false}
|
|
toggleCollapse={mockToggleCollapse}
|
|
/>
|
|
</BrowserRouter>
|
|
);
|
|
|
|
// Operations section visible
|
|
expect(screen.getByText('Dashboard')).toBeInTheDocument();
|
|
expect(screen.getByText('Businesses')).toBeInTheDocument();
|
|
|
|
// System section not visible
|
|
expect(screen.queryByText('System')).not.toBeInTheDocument();
|
|
expect(screen.queryByText('Staff')).not.toBeInTheDocument();
|
|
expect(screen.queryByText('Platform Settings')).not.toBeInTheDocument();
|
|
});
|
|
|
|
it('hides system section and dashboard for platform support', () => {
|
|
render(
|
|
<BrowserRouter>
|
|
<PlatformSidebar
|
|
user={mockPlatformSupport}
|
|
isCollapsed={false}
|
|
toggleCollapse={mockToggleCollapse}
|
|
/>
|
|
</BrowserRouter>
|
|
);
|
|
|
|
// Dashboard not visible for support
|
|
expect(screen.queryByText('Dashboard')).not.toBeInTheDocument();
|
|
|
|
// Operations section visible
|
|
expect(screen.getByText('Businesses')).toBeInTheDocument();
|
|
expect(screen.getByText('Users')).toBeInTheDocument();
|
|
|
|
// System section not visible
|
|
expect(screen.queryByText('System')).not.toBeInTheDocument();
|
|
expect(screen.queryByText('Staff')).not.toBeInTheDocument();
|
|
});
|
|
|
|
it('displays role with underscores replaced by spaces', () => {
|
|
render(
|
|
<BrowserRouter>
|
|
<PlatformSidebar
|
|
user={mockPlatformManager}
|
|
isCollapsed={false}
|
|
toggleCollapse={mockToggleCollapse}
|
|
/>
|
|
</BrowserRouter>
|
|
);
|
|
|
|
expect(screen.getByText('platform manager')).toBeInTheDocument();
|
|
});
|
|
});
|
|
|
|
describe('Collapsed State', () => {
|
|
it('hides text labels when collapsed', () => {
|
|
render(
|
|
<BrowserRouter>
|
|
<PlatformSidebar
|
|
user={mockSuperuser}
|
|
isCollapsed={true}
|
|
toggleCollapse={mockToggleCollapse}
|
|
/>
|
|
</BrowserRouter>
|
|
);
|
|
|
|
// Logo should be visible
|
|
expect(screen.getByTestId('smooth-schedule-logo')).toBeInTheDocument();
|
|
|
|
// Text should be hidden
|
|
expect(screen.queryByText('Smooth Schedule')).not.toBeInTheDocument();
|
|
expect(screen.queryByText('superuser')).not.toBeInTheDocument();
|
|
|
|
// Section headers should show abbreviated versions
|
|
expect(screen.getByText('Ops')).toBeInTheDocument();
|
|
expect(screen.getByText('Sys')).toBeInTheDocument();
|
|
});
|
|
|
|
it('shows full section names when expanded', () => {
|
|
render(
|
|
<BrowserRouter>
|
|
<PlatformSidebar
|
|
user={mockSuperuser}
|
|
isCollapsed={false}
|
|
toggleCollapse={mockToggleCollapse}
|
|
/>
|
|
</BrowserRouter>
|
|
);
|
|
|
|
expect(screen.getByText('Operations')).toBeInTheDocument();
|
|
expect(screen.getByText('System')).toBeInTheDocument();
|
|
expect(screen.queryByText('Ops')).not.toBeInTheDocument();
|
|
expect(screen.queryByText('Sys')).not.toBeInTheDocument();
|
|
});
|
|
|
|
it('applies correct width classes based on collapsed state', () => {
|
|
const { container, rerender } = render(
|
|
<BrowserRouter>
|
|
<PlatformSidebar
|
|
user={mockSuperuser}
|
|
isCollapsed={false}
|
|
toggleCollapse={mockToggleCollapse}
|
|
/>
|
|
</BrowserRouter>
|
|
);
|
|
|
|
const sidebar = container.firstChild as HTMLElement;
|
|
expect(sidebar).toHaveClass('w-64');
|
|
expect(sidebar).not.toHaveClass('w-20');
|
|
|
|
rerender(
|
|
<BrowserRouter>
|
|
<PlatformSidebar
|
|
user={mockSuperuser}
|
|
isCollapsed={true}
|
|
toggleCollapse={mockToggleCollapse}
|
|
/>
|
|
</BrowserRouter>
|
|
);
|
|
|
|
expect(sidebar).toHaveClass('w-20');
|
|
expect(sidebar).not.toHaveClass('w-64');
|
|
});
|
|
});
|
|
|
|
describe('Toggle Collapse Button', () => {
|
|
it('calls toggleCollapse when clicked', async () => {
|
|
const user = userEvent.setup();
|
|
|
|
render(
|
|
<BrowserRouter>
|
|
<PlatformSidebar
|
|
user={mockSuperuser}
|
|
isCollapsed={false}
|
|
toggleCollapse={mockToggleCollapse}
|
|
/>
|
|
</BrowserRouter>
|
|
);
|
|
|
|
const toggleButton = screen.getByRole('button', { name: /collapse sidebar/i });
|
|
await user.click(toggleButton);
|
|
|
|
expect(mockToggleCollapse).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it('has correct aria-label when collapsed', () => {
|
|
render(
|
|
<BrowserRouter>
|
|
<PlatformSidebar
|
|
user={mockSuperuser}
|
|
isCollapsed={true}
|
|
toggleCollapse={mockToggleCollapse}
|
|
/>
|
|
</BrowserRouter>
|
|
);
|
|
|
|
expect(screen.getByRole('button', { name: /expand sidebar/i })).toBeInTheDocument();
|
|
});
|
|
|
|
it('has correct aria-label when expanded', () => {
|
|
render(
|
|
<BrowserRouter>
|
|
<PlatformSidebar
|
|
user={mockSuperuser}
|
|
isCollapsed={false}
|
|
toggleCollapse={mockToggleCollapse}
|
|
/>
|
|
</BrowserRouter>
|
|
);
|
|
|
|
expect(screen.getByRole('button', { name: /collapse sidebar/i })).toBeInTheDocument();
|
|
});
|
|
});
|
|
|
|
describe('Active Link Highlighting', () => {
|
|
it('highlights the active link based on current path', () => {
|
|
render(
|
|
<MemoryRouter initialEntries={['/platform/businesses']}>
|
|
<PlatformSidebar
|
|
user={mockSuperuser}
|
|
isCollapsed={false}
|
|
toggleCollapse={mockToggleCollapse}
|
|
/>
|
|
</MemoryRouter>
|
|
);
|
|
|
|
const businessesLink = screen.getByRole('link', { name: /businesses/i });
|
|
const usersLink = screen.getByRole('link', { name: /^users$/i });
|
|
|
|
// Active link should have active classes
|
|
expect(businessesLink).toHaveClass('bg-gray-700', 'text-white');
|
|
expect(businessesLink).not.toHaveClass('text-gray-400');
|
|
|
|
// Inactive link should have inactive classes
|
|
expect(usersLink).toHaveClass('text-gray-400');
|
|
expect(usersLink).not.toHaveClass('bg-gray-700');
|
|
});
|
|
|
|
it('highlights dashboard link when on dashboard route', () => {
|
|
render(
|
|
<MemoryRouter initialEntries={['/platform/dashboard']}>
|
|
<PlatformSidebar
|
|
user={mockSuperuser}
|
|
isCollapsed={false}
|
|
toggleCollapse={mockToggleCollapse}
|
|
/>
|
|
</MemoryRouter>
|
|
);
|
|
|
|
const dashboardLink = screen.getByRole('link', { name: /dashboard/i });
|
|
expect(dashboardLink).toHaveClass('bg-gray-700', 'text-white');
|
|
});
|
|
|
|
it('highlights link for nested routes', () => {
|
|
render(
|
|
<MemoryRouter initialEntries={['/platform/businesses/123']}>
|
|
<PlatformSidebar
|
|
user={mockSuperuser}
|
|
isCollapsed={false}
|
|
toggleCollapse={mockToggleCollapse}
|
|
/>
|
|
</MemoryRouter>
|
|
);
|
|
|
|
const businessesLink = screen.getByRole('link', { name: /businesses/i });
|
|
expect(businessesLink).toHaveClass('bg-gray-700', 'text-white');
|
|
});
|
|
|
|
it('highlights staff link when on staff route', () => {
|
|
render(
|
|
<MemoryRouter initialEntries={['/platform/staff']}>
|
|
<PlatformSidebar
|
|
user={mockSuperuser}
|
|
isCollapsed={false}
|
|
toggleCollapse={mockToggleCollapse}
|
|
/>
|
|
</MemoryRouter>
|
|
);
|
|
|
|
const staffLink = screen.getByRole('link', { name: /staff/i });
|
|
expect(staffLink).toHaveClass('bg-gray-700', 'text-white');
|
|
});
|
|
|
|
it('highlights help link when on help route', () => {
|
|
render(
|
|
<MemoryRouter initialEntries={['/help/api']}>
|
|
<PlatformSidebar
|
|
user={mockSuperuser}
|
|
isCollapsed={false}
|
|
toggleCollapse={mockToggleCollapse}
|
|
/>
|
|
</MemoryRouter>
|
|
);
|
|
|
|
const apiDocsLink = screen.getByRole('link', { name: /api docs/i });
|
|
expect(apiDocsLink).toHaveClass('bg-gray-700', 'text-white');
|
|
});
|
|
});
|
|
|
|
describe('Navigation Links', () => {
|
|
it('has correct href attributes for all links', () => {
|
|
render(
|
|
<BrowserRouter>
|
|
<PlatformSidebar
|
|
user={mockSuperuser}
|
|
isCollapsed={false}
|
|
toggleCollapse={mockToggleCollapse}
|
|
/>
|
|
</BrowserRouter>
|
|
);
|
|
|
|
expect(screen.getByRole('link', { name: /dashboard/i })).toHaveAttribute('href', '/platform/dashboard');
|
|
expect(screen.getByRole('link', { name: /businesses/i })).toHaveAttribute('href', '/platform/businesses');
|
|
expect(screen.getByRole('link', { name: /^users$/i })).toHaveAttribute('href', '/platform/users');
|
|
expect(screen.getByRole('link', { name: /support/i })).toHaveAttribute('href', '/platform/support');
|
|
expect(screen.getByRole('link', { name: /staff/i })).toHaveAttribute('href', '/platform/staff');
|
|
expect(screen.getByRole('link', { name: /platform settings/i })).toHaveAttribute('href', '/platform/settings');
|
|
expect(screen.getByRole('link', { name: /api docs/i })).toHaveAttribute('href', '/help/api');
|
|
});
|
|
|
|
it('shows title attributes on links for accessibility', () => {
|
|
render(
|
|
<BrowserRouter>
|
|
<PlatformSidebar
|
|
user={mockSuperuser}
|
|
isCollapsed={false}
|
|
toggleCollapse={mockToggleCollapse}
|
|
/>
|
|
</BrowserRouter>
|
|
);
|
|
|
|
expect(screen.getByRole('link', { name: /dashboard/i })).toHaveAttribute('title', 'Platform Dashboard');
|
|
expect(screen.getByRole('link', { name: /businesses/i })).toHaveAttribute('title', 'Businesses');
|
|
expect(screen.getByRole('link', { name: /^users$/i })).toHaveAttribute('title', 'Users');
|
|
});
|
|
});
|
|
|
|
describe('Icons', () => {
|
|
it('renders lucide-react icons for all navigation items', () => {
|
|
const { container } = render(
|
|
<BrowserRouter>
|
|
<PlatformSidebar
|
|
user={mockSuperuser}
|
|
isCollapsed={false}
|
|
toggleCollapse={mockToggleCollapse}
|
|
/>
|
|
</BrowserRouter>
|
|
);
|
|
|
|
// Check that SVG icons are present (lucide-react renders as SVG)
|
|
const svgs = container.querySelectorAll('svg');
|
|
// Should have: logo + icons for each nav item
|
|
expect(svgs.length).toBeGreaterThanOrEqual(10);
|
|
});
|
|
|
|
it('keeps icons visible when collapsed', () => {
|
|
const { container } = render(
|
|
<BrowserRouter>
|
|
<PlatformSidebar
|
|
user={mockSuperuser}
|
|
isCollapsed={true}
|
|
toggleCollapse={mockToggleCollapse}
|
|
/>
|
|
</BrowserRouter>
|
|
);
|
|
|
|
// Icons should still be present when collapsed
|
|
const svgs = container.querySelectorAll('svg');
|
|
expect(svgs.length).toBeGreaterThanOrEqual(10);
|
|
});
|
|
});
|
|
|
|
describe('Responsive Design', () => {
|
|
it('applies flex column layout', () => {
|
|
const { container } = render(
|
|
<BrowserRouter>
|
|
<PlatformSidebar
|
|
user={mockSuperuser}
|
|
isCollapsed={false}
|
|
toggleCollapse={mockToggleCollapse}
|
|
/>
|
|
</BrowserRouter>
|
|
);
|
|
|
|
const sidebar = container.firstChild as HTMLElement;
|
|
expect(sidebar).toHaveClass('flex', 'flex-col', 'h-full');
|
|
});
|
|
|
|
it('applies dark theme colors', () => {
|
|
const { container } = render(
|
|
<BrowserRouter>
|
|
<PlatformSidebar
|
|
user={mockSuperuser}
|
|
isCollapsed={false}
|
|
toggleCollapse={mockToggleCollapse}
|
|
/>
|
|
</BrowserRouter>
|
|
);
|
|
|
|
const sidebar = container.firstChild as HTMLElement;
|
|
expect(sidebar).toHaveClass('bg-gray-900', 'text-white');
|
|
});
|
|
|
|
it('has transition classes for smooth collapse animation', () => {
|
|
const { container } = render(
|
|
<BrowserRouter>
|
|
<PlatformSidebar
|
|
user={mockSuperuser}
|
|
isCollapsed={false}
|
|
toggleCollapse={mockToggleCollapse}
|
|
/>
|
|
</BrowserRouter>
|
|
);
|
|
|
|
const sidebar = container.firstChild as HTMLElement;
|
|
expect(sidebar).toHaveClass('transition-all', 'duration-300');
|
|
});
|
|
});
|
|
|
|
describe('Role-Based Access Control', () => {
|
|
it('shows dashboard for superuser and platform_manager only', () => {
|
|
const { rerender } = render(
|
|
<BrowserRouter>
|
|
<PlatformSidebar
|
|
user={mockSuperuser}
|
|
isCollapsed={false}
|
|
toggleCollapse={mockToggleCollapse}
|
|
/>
|
|
</BrowserRouter>
|
|
);
|
|
expect(screen.queryByText('Dashboard')).toBeInTheDocument();
|
|
|
|
rerender(
|
|
<BrowserRouter>
|
|
<PlatformSidebar
|
|
user={mockPlatformManager}
|
|
isCollapsed={false}
|
|
toggleCollapse={mockToggleCollapse}
|
|
/>
|
|
</BrowserRouter>
|
|
);
|
|
expect(screen.queryByText('Dashboard')).toBeInTheDocument();
|
|
|
|
rerender(
|
|
<BrowserRouter>
|
|
<PlatformSidebar
|
|
user={mockPlatformSupport}
|
|
isCollapsed={false}
|
|
toggleCollapse={mockToggleCollapse}
|
|
/>
|
|
</BrowserRouter>
|
|
);
|
|
expect(screen.queryByText('Dashboard')).not.toBeInTheDocument();
|
|
});
|
|
|
|
it('shows system section only for superuser', () => {
|
|
const { rerender } = render(
|
|
<BrowserRouter>
|
|
<PlatformSidebar
|
|
user={mockSuperuser}
|
|
isCollapsed={false}
|
|
toggleCollapse={mockToggleCollapse}
|
|
/>
|
|
</BrowserRouter>
|
|
);
|
|
expect(screen.queryByText('System')).toBeInTheDocument();
|
|
expect(screen.queryByText('Staff')).toBeInTheDocument();
|
|
|
|
rerender(
|
|
<BrowserRouter>
|
|
<PlatformSidebar
|
|
user={mockPlatformManager}
|
|
isCollapsed={false}
|
|
toggleCollapse={mockToggleCollapse}
|
|
/>
|
|
</BrowserRouter>
|
|
);
|
|
expect(screen.queryByText('System')).not.toBeInTheDocument();
|
|
expect(screen.queryByText('Staff')).not.toBeInTheDocument();
|
|
|
|
rerender(
|
|
<BrowserRouter>
|
|
<PlatformSidebar
|
|
user={mockPlatformSupport}
|
|
isCollapsed={false}
|
|
toggleCollapse={mockToggleCollapse}
|
|
/>
|
|
</BrowserRouter>
|
|
);
|
|
expect(screen.queryByText('System')).not.toBeInTheDocument();
|
|
});
|
|
|
|
it('always shows common operations links for all roles', () => {
|
|
const roles: User[] = [mockSuperuser, mockPlatformManager, mockPlatformSupport];
|
|
|
|
roles.forEach((user) => {
|
|
const { unmount } = render(
|
|
<BrowserRouter>
|
|
<PlatformSidebar
|
|
user={user}
|
|
isCollapsed={false}
|
|
toggleCollapse={mockToggleCollapse}
|
|
/>
|
|
</BrowserRouter>
|
|
);
|
|
|
|
expect(screen.getByText('Businesses')).toBeInTheDocument();
|
|
expect(screen.getByText('Users')).toBeInTheDocument();
|
|
expect(screen.getByText('Support')).toBeInTheDocument();
|
|
|
|
unmount();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('Accessibility', () => {
|
|
it('has semantic HTML structure with nav element', () => {
|
|
const { container } = render(
|
|
<BrowserRouter>
|
|
<PlatformSidebar
|
|
user={mockSuperuser}
|
|
isCollapsed={false}
|
|
toggleCollapse={mockToggleCollapse}
|
|
/>
|
|
</BrowserRouter>
|
|
);
|
|
|
|
const nav = container.querySelector('nav');
|
|
expect(nav).toBeInTheDocument();
|
|
});
|
|
|
|
it('provides proper button label for keyboard users', () => {
|
|
render(
|
|
<BrowserRouter>
|
|
<PlatformSidebar
|
|
user={mockSuperuser}
|
|
isCollapsed={false}
|
|
toggleCollapse={mockToggleCollapse}
|
|
/>
|
|
</BrowserRouter>
|
|
);
|
|
|
|
const button = screen.getByRole('button', { name: /collapse sidebar/i });
|
|
expect(button).toHaveAccessibleName();
|
|
});
|
|
|
|
it('all links have accessible names', () => {
|
|
render(
|
|
<BrowserRouter>
|
|
<PlatformSidebar
|
|
user={mockSuperuser}
|
|
isCollapsed={false}
|
|
toggleCollapse={mockToggleCollapse}
|
|
/>
|
|
</BrowserRouter>
|
|
);
|
|
|
|
const links = screen.getAllByRole('link');
|
|
links.forEach((link) => {
|
|
expect(link).toHaveAccessibleName();
|
|
});
|
|
});
|
|
|
|
it('maintains focus visibility for keyboard navigation', () => {
|
|
const { container } = render(
|
|
<BrowserRouter>
|
|
<PlatformSidebar
|
|
user={mockSuperuser}
|
|
isCollapsed={false}
|
|
toggleCollapse={mockToggleCollapse}
|
|
/>
|
|
</BrowserRouter>
|
|
);
|
|
|
|
const button = screen.getByRole('button', { name: /collapse sidebar/i });
|
|
expect(button).toHaveClass('focus:outline-none');
|
|
});
|
|
});
|
|
|
|
describe('Edge Cases', () => {
|
|
it('handles user with empty name gracefully', () => {
|
|
const userWithoutName: User = {
|
|
...mockSuperuser,
|
|
name: '',
|
|
};
|
|
|
|
render(
|
|
<BrowserRouter>
|
|
<PlatformSidebar
|
|
user={userWithoutName}
|
|
isCollapsed={false}
|
|
toggleCollapse={mockToggleCollapse}
|
|
/>
|
|
</BrowserRouter>
|
|
);
|
|
|
|
// Should still render without crashing
|
|
expect(screen.getByTestId('smooth-schedule-logo')).toBeInTheDocument();
|
|
});
|
|
|
|
it('handles missing translation gracefully', () => {
|
|
// Translation mock should return the key if translation is missing
|
|
render(
|
|
<BrowserRouter>
|
|
<PlatformSidebar
|
|
user={mockSuperuser}
|
|
isCollapsed={false}
|
|
toggleCollapse={mockToggleCollapse}
|
|
/>
|
|
</BrowserRouter>
|
|
);
|
|
|
|
// Should render without errors even with missing translations
|
|
expect(screen.getByTestId('smooth-schedule-logo')).toBeInTheDocument();
|
|
});
|
|
|
|
it('handles rapid collapse/expand toggling', async () => {
|
|
const user = userEvent.setup();
|
|
|
|
render(
|
|
<BrowserRouter>
|
|
<PlatformSidebar
|
|
user={mockSuperuser}
|
|
isCollapsed={false}
|
|
toggleCollapse={mockToggleCollapse}
|
|
/>
|
|
</BrowserRouter>
|
|
);
|
|
|
|
const button = screen.getByRole('button', { name: /collapse sidebar/i });
|
|
|
|
// Rapidly click multiple times
|
|
await user.click(button);
|
|
await user.click(button);
|
|
await user.click(button);
|
|
|
|
expect(mockToggleCollapse).toHaveBeenCalledTimes(3);
|
|
});
|
|
});
|
|
});
|