diff --git a/frontend/src/routes/(app)/recipes/+page.svelte b/frontend/src/routes/(app)/recipes/+page.svelte index 1faa0a3..36632de 100644 --- a/frontend/src/routes/(app)/recipes/+page.svelte +++ b/frontend/src/routes/(app)/recipes/+page.svelte @@ -1 +1,54 @@ -

Rezepte

+ + + + Rezepte — Mealplan + + +
+
+

Rezepte

+ Rezept hinzufügen +
+ + + + (activeFilter = f)} /> + + +
diff --git a/frontend/src/routes/(app)/recipes/page.test.ts b/frontend/src/routes/(app)/recipes/page.test.ts new file mode 100644 index 0000000..b55545e --- /dev/null +++ b/frontend/src/routes/(app)/recipes/page.test.ts @@ -0,0 +1,74 @@ +import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; +import { render, screen } from '@testing-library/svelte'; +import { userEvent } from '@testing-library/user-event'; +import Page from './+page.svelte'; + +const mockData = { + recipes: [ + { id: 'r1', name: 'Spaghetti Bolognese', cookTimeMin: 30, effort: 'Easy' }, + { id: 'r2', name: 'Chicken Curry', cookTimeMin: 45, effort: 'Medium' }, + { id: 'r3', name: 'Gemüsesuppe', cookTimeMin: 20, effort: 'Easy' } + ] +}; + +describe('recipe library page', () => { + it('renders all recipe cards initially', () => { + render(Page, { props: { data: mockData } }); + expect(screen.getByText('Spaghetti Bolognese')).toBeInTheDocument(); + expect(screen.getByText('Chicken Curry')).toBeInTheDocument(); + expect(screen.getByText('Gemüsesuppe')).toBeInTheDocument(); + }); + + it('renders the page title', () => { + render(Page, { props: { data: mockData } }); + expect(document.title).toBe('Rezepte — Mealplan'); + }); + + it('renders a link to add a new recipe', () => { + render(Page, { props: { data: mockData } }); + const addLink = screen.getByRole('link', { name: /rezept hinzufügen/i }); + expect(addLink).toHaveAttribute('href', '/recipes/new'); + }); + + it('filters recipes by search term', async () => { + const user = userEvent.setup(); + render(Page, { props: { data: mockData } }); + + const searchInput = screen.getByPlaceholderText(/suchen/i); + await user.type(searchInput, 'Curry'); + + expect(screen.getByText('Chicken Curry')).toBeInTheDocument(); + expect(screen.queryByText('Spaghetti Bolognese')).not.toBeInTheDocument(); + }); + + it('filters recipes by effort chip', async () => { + const user = userEvent.setup(); + render(Page, { props: { data: mockData } }); + + await user.click(screen.getByRole('button', { name: 'Mittel' })); + + expect(screen.getByText('Chicken Curry')).toBeInTheDocument(); + expect(screen.queryByText('Spaghetti Bolognese')).not.toBeInTheDocument(); + }); + + it('shows empty state when no recipes match search', async () => { + const user = userEvent.setup(); + render(Page, { props: { data: mockData } }); + + const searchInput = screen.getByPlaceholderText(/suchen/i); + await user.type(searchInput, 'xyznotexist'); + + expect(screen.getByText(/keine rezepte/i)).toBeInTheDocument(); + }); + + it('renders filter chips', () => { + render(Page, { props: { data: mockData } }); + expect(screen.getByRole('button', { name: 'Alle' })).toBeInTheDocument(); + expect(screen.getByRole('button', { name: 'Leicht' })).toBeInTheDocument(); + }); + + it('renders empty state page when no recipes at all', () => { + render(Page, { props: { data: { recipes: [] } } }); + expect(screen.getByText(/keine rezepte/i)).toBeInTheDocument(); + }); +});