diff --git a/frontend/src/lib/recipes/RecipeCard.svelte b/frontend/src/lib/recipes/RecipeCard.svelte new file mode 100644 index 0000000..97f9d81 --- /dev/null +++ b/frontend/src/lib/recipes/RecipeCard.svelte @@ -0,0 +1,66 @@ + + + +
+ {#if recipe.heroImageUrl} + {recipe.name} + {:else} +
+ +
+ {/if} +
+ +
+

{recipe.name}

+ {#if metadata} +

{metadata}

+ {/if} +
+
diff --git a/frontend/src/lib/recipes/RecipeCard.test.ts b/frontend/src/lib/recipes/RecipeCard.test.ts new file mode 100644 index 0000000..f9792a4 --- /dev/null +++ b/frontend/src/lib/recipes/RecipeCard.test.ts @@ -0,0 +1,62 @@ +import { describe, it, expect, vi } from 'vitest'; +import { render, screen } from '@testing-library/svelte'; +import { userEvent } from '@testing-library/user-event'; +import RecipeCard from './RecipeCard.svelte'; + +const mockRecipe = { + id: 'recipe-1', + name: 'Spaghetti Bolognese', + cookTimeMin: 30, + effort: 'Easy', + heroImageUrl: undefined +}; + +describe('RecipeCard', () => { + it('renders the recipe name', () => { + render(RecipeCard, { props: { recipe: mockRecipe } }); + expect(screen.getByText('Spaghetti Bolognese')).toBeInTheDocument(); + }); + + it('renders cook time when present', () => { + render(RecipeCard, { props: { recipe: mockRecipe } }); + expect(screen.getByText(/30/)).toBeInTheDocument(); + }); + + it('renders effort when present', () => { + render(RecipeCard, { props: { recipe: mockRecipe } }); + expect(screen.getByText(/easy/i)).toBeInTheDocument(); + }); + + it('shows placeholder when no heroImageUrl', () => { + render(RecipeCard, { props: { recipe: { ...mockRecipe, heroImageUrl: undefined } } }); + expect(screen.queryByRole('img')).not.toBeInTheDocument(); + expect(document.querySelector('[data-testid="image-placeholder"]')).toBeInTheDocument(); + }); + + it('shows image when heroImageUrl is provided', () => { + render(RecipeCard, { + props: { recipe: { ...mockRecipe, heroImageUrl: '/uploads/test.jpg' } } + }); + const img = screen.getByRole('img'); + expect(img).toHaveAttribute('src', '/uploads/test.jpg'); + expect(img).toHaveAttribute('alt', 'Spaghetti Bolognese'); + }); + + it('wraps in a link to the recipe detail page', () => { + render(RecipeCard, { props: { recipe: mockRecipe } }); + const link = screen.getByRole('link'); + expect(link).toHaveAttribute('href', '/recipes/recipe-1'); + }); + + it('applies compact image height when compact prop is true', () => { + render(RecipeCard, { props: { recipe: mockRecipe, compact: true } }); + const imageArea = document.querySelector('[data-testid="image-area"]'); + expect(imageArea?.className).toContain('h-[64px]'); + }); + + it('applies full image height when compact prop is false', () => { + render(RecipeCard, { props: { recipe: mockRecipe, compact: false } }); + const imageArea = document.querySelector('[data-testid="image-area"]'); + expect(imageArea?.className).toContain('h-[100px]'); + }); +});