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
+
+
+
+
+
+
+
+
(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();
+ });
+});