Files
fotospiel-app/resources/js/admin/mobile/__tests__/EventsPage.test.tsx
Codex Agent e28eb9a90b
Some checks failed
linter / quality (push) Has been cancelled
tests / ci (push) Has been cancelled
tests / ui (push) Has been cancelled
Fix event search filtering
2026-01-20 12:41:14 +01:00

196 lines
6.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React from 'react';
import { describe, expect, it, vi } from 'vitest';
import { act, fireEvent, render, screen } from '@testing-library/react';
const navigateMock = vi.fn();
const authState = {
user: { role: 'tenant_admin' },
};
vi.mock('react-router-dom', () => ({
useNavigate: () => navigateMock,
}));
vi.mock('react-i18next', () => ({
useTranslation: () => ({
t: (key: string) => {
const translations: Record<string, string> = {
'events.list.title': 'Your events',
'events.list.subtitle': 'Plan memorable moments. Manage everything around your events here.',
'events.list.overview.title': 'Overview',
'events.list.overview.empty': 'No events yet create your first one to get started.',
'events.list.filters.all': 'All',
'events.list.filters.upcoming': 'Upcoming',
'events.list.filters.draft': 'Draft',
'events.list.filters.past': 'Past',
'events.list.actions.create': 'New event',
'events.list.actions.open': 'Open event',
'events.list.empty.filtered': 'No events match this filter.',
'events.list.empty.filteredHint': 'Try a different status or clear your search.',
'events.list.stats.photos': 'Photos',
'events.list.stats.guests': 'Guests',
'events.list.stats.tasks': 'Photo tasks',
'events.workspace.fields.status': 'Status',
'events.detail.pickEvent': 'Select event',
'events.detail.dateTbd': 'Date tbd',
'events.detail.locationPlaceholder': 'Location',
'events.placeholders.untitled': 'Untitled event',
'events.errors.loadFailed': 'Event konnte nicht geladen werden.',
};
return translations[key] ?? key;
},
i18n: {
language: 'en',
},
}),
}));
vi.mock('../../api', () => ({
getEvents: vi.fn().mockResolvedValue([
{
id: 1,
name: 'Demo Event',
slug: 'demo-event',
event_date: '2026-02-19',
settings: {},
},
{
id: 2,
name: { en: 'Summer Gala' },
slug: 'summer-gala',
event_date: '2026-06-01',
settings: {},
},
]),
}));
vi.mock('../../auth/tokens', () => ({
isAuthError: () => false,
}));
vi.mock('../../auth/context', () => ({
useAuth: () => authState,
}));
vi.mock('../../lib/apiError', () => ({
getApiErrorMessage: () => 'error',
}));
vi.mock('../../lib/eventFilters', () => ({
buildEventStatusCounts: () => ({ all: 1, upcoming: 1, draft: 0, past: 0 }),
filterEventsByStatus: (events: any[]) => events,
resolveEventStatusKey: () => 'upcoming',
}));
vi.mock('../../lib/eventListStats', () => ({
buildEventListStats: () => ({ photos: 2, guests: 3, tasks: 4 }),
}));
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', () => ({
PillBadge: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
CTAButton: ({ label }: { label: string }) => <button type="button">{label}</button>,
FloatingActionButton: ({ label }: { label: string }) => <div>{label}</div>,
SkeletonCard: () => <div>Loading...</div>,
}));
vi.mock('../components/FormControls', () => ({
MobileInput: ({ compact: _compact, ...props }: React.InputHTMLAttributes<HTMLInputElement>) => <input {...props} />,
}));
vi.mock('@tamagui/card', () => ({
Card: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
}));
vi.mock('@tamagui/scroll-view', () => ({
ScrollView: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
}));
vi.mock('@tamagui/react-native-web-lite', () => ({
Pressable: ({ children, onPress }: { children: React.ReactNode; onPress?: () => void }) => (
<button type="button" onClick={onPress}>
{children}
</button>
),
}));
vi.mock('@tamagui/toggle-group', () => ({
ToggleGroup: Object.assign(({ children }: { children: React.ReactNode }) => <div>{children}</div>, {
Item: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
}),
}));
vi.mock('@tamagui/separator', () => ({
Separator: () => <div />,
}));
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('../theme', () => ({
useAdminTheme: () => ({
text: '#111827',
muted: '#6b7280',
subtle: '#94a3b8',
border: '#e5e7eb',
primary: '#ff5a5f',
danger: '#dc2626',
surface: '#ffffff',
surfaceMuted: '#f9fafb',
accentSoft: '#eef2ff',
accent: '#6366f1',
shadow: 'rgba(15,23,42,0.12)',
}),
}));
import MobileEventsPage from '../EventsPage';
describe('MobileEventsPage', () => {
it('renders filters and event list', async () => {
render(<MobileEventsPage />);
expect(await screen.findByText('Overview')).toBeInTheDocument();
expect(screen.getByText('Status')).toBeInTheDocument();
expect(screen.getByText('Demo Event')).toBeInTheDocument();
});
it('hides create actions for members', async () => {
authState.user = { role: 'member' };
render(<MobileEventsPage />);
expect(await screen.findByText('Demo Event')).toBeInTheDocument();
expect(screen.queryByText('New event')).not.toBeInTheDocument();
authState.user = { role: 'tenant_admin' };
});
it('filters events by search query', async () => {
render(<MobileEventsPage />);
expect(await screen.findByText('Demo Event')).toBeInTheDocument();
expect(screen.getByText('Summer Gala')).toBeInTheDocument();
await act(async () => {
fireEvent.change(screen.getByPlaceholderText('Select event'), { target: { value: 'Summer' } });
});
expect(screen.getByText('Summer Gala')).toBeInTheDocument();
expect(screen.queryByText('Demo Event')).not.toBeInTheDocument();
});
});