180 lines
5.0 KiB
TypeScript
180 lines
5.0 KiB
TypeScript
import React from 'react';
|
|
import { describe, expect, it, vi } from 'vitest';
|
|
import { render, screen } from '@testing-library/react';
|
|
|
|
const navigateMock = vi.fn();
|
|
const selectEventMock = vi.fn();
|
|
|
|
vi.mock('react-router-dom', () => ({
|
|
useNavigate: () => navigateMock,
|
|
useLocation: () => ({ search: '', pathname: '/event-admin/mobile/events/demo-event/control-room' }),
|
|
useParams: () => ({ slug: 'demo-event' }),
|
|
}));
|
|
|
|
const tMock = (key: string, fallback?: string | Record<string, unknown>, options?: Record<string, unknown>) => {
|
|
if (typeof fallback === 'string') {
|
|
return fallback;
|
|
}
|
|
if (fallback && typeof fallback === 'object' && typeof fallback.defaultValue === 'string') {
|
|
return fallback.defaultValue;
|
|
}
|
|
if (options?.defaultValue) {
|
|
return String(options.defaultValue);
|
|
}
|
|
return key;
|
|
};
|
|
|
|
vi.mock('react-i18next', () => ({
|
|
useTranslation: () => ({
|
|
t: tMock,
|
|
}),
|
|
}));
|
|
|
|
vi.mock('../../auth/context', () => ({
|
|
useAuth: () => ({ user: { role: 'tenant_admin' } }),
|
|
}));
|
|
|
|
vi.mock('../../context/EventContext', () => ({
|
|
useEventContext: () => ({
|
|
activeEvent: { slug: 'demo-event' },
|
|
selectEvent: selectEventMock,
|
|
}),
|
|
}));
|
|
|
|
vi.mock('../hooks/useOnlineStatus', () => ({
|
|
useOnlineStatus: () => true,
|
|
}));
|
|
|
|
vi.mock('../hooks/useBackNavigation', () => ({
|
|
useBackNavigation: () => undefined,
|
|
}));
|
|
|
|
vi.mock('../components/MobileShell', () => ({
|
|
MobileShell: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
|
|
HeaderActionButton: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
|
|
}));
|
|
|
|
vi.mock('../components/Primitives', () => ({
|
|
MobileCard: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
|
|
CTAButton: ({ label, onPress }: { label: string; onPress?: () => void }) => (
|
|
<button type="button" onClick={onPress}>
|
|
{label}
|
|
</button>
|
|
),
|
|
SkeletonCard: () => <div>Loading...</div>,
|
|
}));
|
|
|
|
vi.mock('../components/FormControls', () => ({
|
|
MobileField: ({ label, children }: { label: string; children: React.ReactNode }) => (
|
|
<label>
|
|
{label}
|
|
{children}
|
|
</label>
|
|
),
|
|
MobileSelect: (props: React.SelectHTMLAttributes<HTMLSelectElement>) => <select {...props} />,
|
|
}));
|
|
|
|
vi.mock('../components/LegalConsentSheet', () => ({
|
|
LegalConsentSheet: () => null,
|
|
}));
|
|
|
|
vi.mock('../components/LimitWarnings', () => ({
|
|
LimitWarnings: () => null,
|
|
}));
|
|
|
|
vi.mock('react-hot-toast', () => ({
|
|
default: {
|
|
error: vi.fn(),
|
|
success: vi.fn(),
|
|
},
|
|
}));
|
|
|
|
vi.mock('@tamagui/stacks', () => ({
|
|
YStack: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
|
|
XStack: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
|
|
}));
|
|
|
|
vi.mock('@tamagui/text', () => ({
|
|
SizableText: ({ children }: { children: React.ReactNode }) => <span>{children}</span>,
|
|
}));
|
|
|
|
vi.mock('@tamagui/react-native-web-lite', () => ({
|
|
Pressable: ({ children, onPress }: { children: React.ReactNode; onPress?: () => void }) => (
|
|
<button type="button" onClick={onPress}>
|
|
{children}
|
|
</button>
|
|
),
|
|
}));
|
|
|
|
vi.mock('../theme', () => ({
|
|
useAdminTheme: () => ({
|
|
textStrong: '#111827',
|
|
text: '#111827',
|
|
muted: '#6b7280',
|
|
border: '#e5e7eb',
|
|
accentSoft: '#eef2ff',
|
|
accent: '#6366f1',
|
|
danger: '#dc2626',
|
|
surfaceMuted: '#f9fafb',
|
|
}),
|
|
}));
|
|
|
|
vi.mock('../lib/photoModerationQueue', () => ({
|
|
enqueuePhotoAction: vi.fn().mockReturnValue([]),
|
|
loadPhotoQueue: () => [],
|
|
removePhotoAction: vi.fn((queue: unknown[]) => queue),
|
|
replacePhotoQueue: vi.fn((queue: unknown[]) => queue),
|
|
}));
|
|
|
|
vi.mock('../lib/haptics', () => ({
|
|
triggerHaptic: () => undefined,
|
|
}));
|
|
|
|
vi.mock('../../api', () => ({
|
|
approveAndLiveShowPhoto: vi.fn(),
|
|
approveLiveShowPhoto: vi.fn(),
|
|
clearLiveShowPhoto: vi.fn(),
|
|
createEventAddonCheckout: vi.fn(),
|
|
featurePhoto: vi.fn(),
|
|
getAddonCatalog: vi.fn().mockResolvedValue([]),
|
|
getEventPhotos: vi.fn().mockResolvedValue({
|
|
photos: [
|
|
{
|
|
id: 1,
|
|
filename: 'photo.jpg',
|
|
original_name: 'Photo 1',
|
|
mime_type: 'image/jpeg',
|
|
size: 120,
|
|
url: '/full.jpg',
|
|
thumbnail_url: '/thumb.jpg',
|
|
status: 'pending',
|
|
live_status: null,
|
|
is_featured: false,
|
|
likes_count: 0,
|
|
uploaded_at: '2024-01-01T00:00:00Z',
|
|
uploader_name: 'Guest',
|
|
},
|
|
],
|
|
limits: null,
|
|
meta: { last_page: 1 },
|
|
}),
|
|
getEvents: vi.fn().mockResolvedValue([]),
|
|
getLiveShowQueue: vi.fn().mockResolvedValue({ photos: [], meta: { last_page: 1 } }),
|
|
rejectLiveShowPhoto: vi.fn(),
|
|
unfeaturePhoto: vi.fn(),
|
|
updatePhotoStatus: vi.fn(),
|
|
updatePhotoVisibility: vi.fn(),
|
|
}));
|
|
|
|
import MobileEventControlRoomPage from '../EventControlRoomPage';
|
|
|
|
describe('MobileEventControlRoomPage', () => {
|
|
it('renders compact grid actions for moderation photos', async () => {
|
|
render(<MobileEventControlRoomPage />);
|
|
|
|
expect(await screen.findByText('Approve')).toBeInTheDocument();
|
|
expect(screen.getByText('Hide')).toBeInTheDocument();
|
|
expect(screen.getByText('Set highlight')).toBeInTheDocument();
|
|
});
|
|
});
|