- Extract RecipeSummary type to $lib/recipes/types.ts (was duplicated in 3 files) - Fix +page.svelte header link: replace Skeleton UI classes with design system tokens - Fix h1: use font-[var(--font-display)] and correct size - Fix FilterChipRow: text-[11px] → text-[13px] + tracking-[0.04em] per design system - Fix RecipeCard metadata: text-[11px] → text-[12px] for readability - Remove unused imports (vi, beforeEach, afterEach) from page.test.ts - Add combined search + effort filter test - Add reset-to-Alle filter test Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
100 lines
3.6 KiB
TypeScript
100 lines
3.6 KiB
TypeScript
import { describe, it, expect } 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();
|
|
});
|
|
|
|
it('applies search and effort filter together', async () => {
|
|
const user = userEvent.setup();
|
|
render(Page, { props: { data: mockData } });
|
|
|
|
await user.click(screen.getByRole('button', { name: 'Leicht' }));
|
|
const searchInput = screen.getByPlaceholderText(/suchen/i);
|
|
await user.type(searchInput, 'Gemüse');
|
|
|
|
expect(screen.getByText('Gemüsesuppe')).toBeInTheDocument();
|
|
expect(screen.queryByText('Spaghetti Bolognese')).not.toBeInTheDocument();
|
|
expect(screen.queryByText('Chicken Curry')).not.toBeInTheDocument();
|
|
});
|
|
|
|
it('resets to all recipes when Alle chip is clicked', async () => {
|
|
const user = userEvent.setup();
|
|
render(Page, { props: { data: mockData } });
|
|
|
|
await user.click(screen.getByRole('button', { name: 'Mittel' }));
|
|
expect(screen.queryByText('Spaghetti Bolognese')).not.toBeInTheDocument();
|
|
|
|
await user.click(screen.getByRole('button', { name: 'Alle' }));
|
|
expect(screen.getByText('Spaghetti Bolognese')).toBeInTheDocument();
|
|
expect(screen.getByText('Chicken Curry')).toBeInTheDocument();
|
|
});
|
|
});
|