import React from 'react'; import { beforeEach, describe, expect, it, vi } from 'vitest'; import { act, render, screen } from '@testing-library/react'; import { MemoryRouter } from 'react-router-dom'; import type { EventContextValue } from '../../../context/EventContext'; import type { TenantEvent } from '../../../api'; vi.mock('react-i18next', () => ({ useTranslation: () => ({ t: (key: string, fallback?: string) => fallback ?? key, i18n: { language: 'en-GB' } }), })); vi.mock('@tamagui/core', () => ({ useTheme: () => ({ background: { val: '#FFF8F5' }, surface: { val: '#ffffff' }, borderColor: { val: '#e5e7eb' }, color: { val: '#1f2937' }, gray: { val: '#6b7280' }, red10: { val: '#b91c1c' }, shadowColor: { val: 'rgba(0,0,0,0.12)' }, primary: { val: '#FF5A5F' }, }), })); vi.mock('@tamagui/stacks', () => ({ YStack: ({ children, ...props }: { children: React.ReactNode }) =>
{children}
, XStack: ({ children, ...props }: { children: React.ReactNode }) =>
{children}
, })); vi.mock('@tamagui/text', () => ({ SizableText: ({ children, ...props }: { children: React.ReactNode }) => {children}, })); vi.mock('@tamagui/react-native-web-lite', () => ({ Pressable: ({ children, onPress, ...props }: { children: React.ReactNode; onPress?: () => void }) => ( ), })); vi.mock('tamagui', () => ({ Separator: ({ children }: { children?: React.ReactNode }) =>
{children}
, Tabs: Object.assign(({ children }: { children: React.ReactNode }) =>
{children}
, { List: ({ children }: { children: React.ReactNode }) =>
{children}
, Tab: ({ children }: { children: React.ReactNode }) =>
{children}
, Content: ({ children }: { children: React.ReactNode }) =>
{children}
, }), })); vi.mock('../BottomNav', () => ({ BottomNav: () =>
, NavKey: {}, })); vi.mock('../UserMenuSheet', () => ({ UserMenuSheet: () =>
, })); const baseEvent: TenantEvent = { id: 1, name: 'Test Event', slug: 'event-1', event_date: '2024-01-01', event_type_id: null, event_type: null, status: 'published', settings: {}, }; const eventContext: EventContextValue = { events: [], isLoading: false, isError: false, activeEvent: baseEvent, activeSlug: baseEvent.slug, hasMultipleEvents: false, hasEvents: true, selectEvent: vi.fn(), refetch: vi.fn(), }; vi.mock('../../../context/EventContext', () => ({ useEventContext: () => eventContext, })); vi.mock('../../../auth/context', () => ({ useAuth: () => ({ user: { role: 'tenant_admin' } }), })); vi.mock('../../hooks/useMobileNav', () => ({ useMobileNav: () => ({ go: vi.fn(), slug: 'event-1' }), })); vi.mock('../../hooks/useNotificationsBadge', () => ({ useNotificationsBadge: () => ({ count: 0 }), })); vi.mock('../../hooks/useOnlineStatus', () => ({ useOnlineStatus: () => true, })); vi.mock('../../../api', () => ({ getEvents: vi.fn().mockResolvedValue([]), })); vi.mock('../../lib/tabHistory', () => ({ setTabHistory: vi.fn(), })); vi.mock('../../lib/photoModerationQueue', () => ({ loadPhotoQueue: vi.fn(() => []), })); vi.mock('../../lib/queueStatus', () => ({ countQueuedPhotoActions: vi.fn(() => 0), })); vi.mock('../../theme', () => ({ useAdminTheme: () => ({ background: '#FFF8F5', surface: '#ffffff', border: '#e5e7eb', text: '#1f2937', muted: '#6b7280', warningBg: '#fff7ed', warningText: '#92400e', primary: '#FF5A5F', danger: '#b91c1c', shadow: 'rgba(0,0,0,0.12)', glassSurface: 'rgba(255,255,255,0.8)', glassSurfaceStrong: 'rgba(255,255,255,0.9)', glassBorder: 'rgba(229,231,235,0.7)', glassShadow: 'rgba(15,23,42,0.14)', appBackground: 'linear-gradient(180deg, #f7fafc, #eef3f7)', }), })); import { MobileShell } from '../MobileShell'; import { ADMIN_EVENTS_PATH } from '../../../constants'; describe('MobileShell', () => { beforeEach(() => { window.matchMedia = vi.fn().mockReturnValue({ matches: false, addEventListener: vi.fn(), removeEventListener: vi.fn(), }); document.title = ''; eventContext.events = []; eventContext.hasMultipleEvents = false; eventContext.activeEvent = { ...baseEvent }; eventContext.activeSlug = baseEvent.slug; }); it('sets the document title with the app prefix', async () => { await act(async () => { render(
Body
); }); expect(document.title).toBe('Fotospiel.App Event Admin ยท Dashboard'); }); it('renders quick QR as icon-only button', async () => { await act(async () => { render(
Body
); }); expect(screen.getByLabelText('Quick QR')).toBeInTheDocument(); expect(screen.queryByText('Quick QR')).not.toBeInTheDocument(); }); it('hides the event context on compact headers', async () => { window.matchMedia = vi.fn().mockReturnValue({ matches: true, addEventListener: vi.fn(), removeEventListener: vi.fn(), }); await act(async () => { render(
Body
); }); expect(screen.queryByText('Test Event')).not.toBeInTheDocument(); }); it('shows the event switcher when multiple events are available', async () => { eventContext.events = [ { ...baseEvent }, { ...baseEvent, id: 2, slug: 'event-2', name: 'Second Event', event_date: '2024-02-01', }, ]; await act(async () => { render(
Body
); }); expect(screen.getByLabelText('Switch event')).toBeInTheDocument(); }); it('hides the event switcher on the events list page', async () => { eventContext.events = [ { ...baseEvent }, { ...baseEvent, id: 2, slug: 'event-2', name: 'Second Event', event_date: '2024-02-01', }, ]; await act(async () => { render(
Body
); }); expect(screen.queryByLabelText('Switch event')).not.toBeInTheDocument(); }); });