From c7e56a173d92049524e2385d8a5bd110859b0ea2 Mon Sep 17 00:00:00 2001 From: Marcel Raddatz Date: Fri, 3 Apr 2026 09:57:36 +0200 Subject: [PATCH] feat(recipes): add IngredientList component (read-only) Co-Authored-By: Claude Sonnet 4.6 --- .../src/lib/recipes/IngredientList.svelte | 32 ++++++++++++++ .../src/lib/recipes/IngredientList.test.ts | 44 +++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 frontend/src/lib/recipes/IngredientList.svelte create mode 100644 frontend/src/lib/recipes/IngredientList.test.ts diff --git a/frontend/src/lib/recipes/IngredientList.svelte b/frontend/src/lib/recipes/IngredientList.svelte new file mode 100644 index 0000000..3a15099 --- /dev/null +++ b/frontend/src/lib/recipes/IngredientList.svelte @@ -0,0 +1,32 @@ + + +
+

+ Zutaten +

+ +
    + {#each ingredients as ingredient (ingredient.ingredientId ?? ingredient.name)} +
  • + {#if ingredient.quantity != null} + + {ingredient.quantity}{ingredient.unit != null ? ` ${ingredient.unit}` : ''} + + {/if} + {ingredient.name} +
  • + {/each} +
+
diff --git a/frontend/src/lib/recipes/IngredientList.test.ts b/frontend/src/lib/recipes/IngredientList.test.ts new file mode 100644 index 0000000..927348a --- /dev/null +++ b/frontend/src/lib/recipes/IngredientList.test.ts @@ -0,0 +1,44 @@ +import { describe, it, expect } from 'vitest'; +import { render, screen } from '@testing-library/svelte'; +import IngredientList from './IngredientList.svelte'; + +const mockIngredients = [ + { ingredientId: 'i1', name: 'Spaghetti', quantity: 200, unit: 'g' }, + { ingredientId: 'i2', name: 'Hackfleisch', quantity: 400, unit: 'g' }, + { ingredientId: 'i3', name: 'Salz', quantity: undefined, unit: undefined } +]; + +describe('IngredientList', () => { + it('renders the section heading', () => { + render(IngredientList, { props: { ingredients: mockIngredients } }); + expect(screen.getByText(/zutaten/i)).toBeInTheDocument(); + }); + + it('renders a row for each ingredient', () => { + render(IngredientList, { props: { ingredients: mockIngredients } }); + expect(screen.getByText('Spaghetti')).toBeInTheDocument(); + expect(screen.getByText('Hackfleisch')).toBeInTheDocument(); + expect(screen.getByText('Salz')).toBeInTheDocument(); + }); + + it('renders quantity and unit when present', () => { + render(IngredientList, { props: { ingredients: mockIngredients } }); + expect(screen.getByText('200 g')).toBeInTheDocument(); + expect(screen.getByText('400 g')).toBeInTheDocument(); + }); + + it('renders no quantity when not present', () => { + render(IngredientList, { props: { ingredients: mockIngredients } }); + expect(screen.queryByText('undefined')).not.toBeInTheDocument(); + }); + + it('has no remove buttons (read-only)', () => { + render(IngredientList, { props: { ingredients: mockIngredients } }); + expect(screen.queryByRole('button')).not.toBeInTheDocument(); + }); + + it('renders empty state when ingredients array is empty', () => { + render(IngredientList, { props: { ingredients: [] } }); + expect(screen.getByText(/zutaten/i)).toBeInTheDocument(); + }); +});