fix(planner): address all PR review blockers
- Fix logic bug `{#if !isPlanner === false}` - view/cook buttons now visible for all roles, swap only for planner
- Convert Tauschen from dead button to link with suggestions href
- Add week.ts unit tests (23 tests covering getWeekStart Sunday edge case, prevWeek/nextWeek, weekDays, isToday, formatWeekRange)
- Fix isToday to use UTC consistently (.toISOString().slice(0,10)) instead of local date
- Add server-side role guard to createPlan action (403 for members)
- Add weekStart format validation in createPlan action
- Add isSelected prop to DayMealCard with green treatment
- Make variety banner sticky on mobile (always visible per spec)
- Add day name abbreviation above date badge in desktop column headers
- Remove placeholder Navigation text from desktop sidebar
- Add aria-label to desktop empty tile buttons
- Add variety score partial failure test, multiple overlaps test, WeekStrip today+selected test
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -14,16 +14,22 @@ describe('DayMealCard', () => {
|
||||
expect(screen.getByText('Pasta Bolognese')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('shows Cook now and Swap buttons when not readonly', () => {
|
||||
it('shows Cook now and Tauschen links when not readonly', () => {
|
||||
render(DayMealCard, { props: { slot, isToday: false, readonly: false } });
|
||||
expect(screen.getByRole('link', { name: /Jetzt kochen/i })).toBeTruthy();
|
||||
expect(screen.getByRole('button', { name: /Tauschen/i })).toBeTruthy();
|
||||
expect(screen.getByRole('link', { name: /Tauschen/i })).toBeTruthy();
|
||||
});
|
||||
|
||||
it('hides action buttons when readonly', () => {
|
||||
it('Tauschen link navigates to suggestions for the slot day', () => {
|
||||
render(DayMealCard, { props: { slot, isToday: false, readonly: false } });
|
||||
const link = screen.getByRole('link', { name: /Tauschen/i });
|
||||
expect(link.getAttribute('href')).toContain('2026-03-30');
|
||||
});
|
||||
|
||||
it('hides action links when readonly', () => {
|
||||
render(DayMealCard, { props: { slot, isToday: false, readonly: true } });
|
||||
expect(screen.queryByRole('link', { name: /Jetzt kochen/i })).toBeNull();
|
||||
expect(screen.queryByRole('button', { name: /Tauschen/i })).toBeNull();
|
||||
expect(screen.queryByRole('link', { name: /Tauschen/i })).toBeNull();
|
||||
});
|
||||
|
||||
it('applies today styling when isToday is true', () => {
|
||||
@@ -32,6 +38,12 @@ describe('DayMealCard', () => {
|
||||
expect(card.getAttribute('data-today')).toBe('true');
|
||||
});
|
||||
|
||||
it('applies selected styling when isSelected is true and not today', () => {
|
||||
render(DayMealCard, { props: { slot, isToday: false, isSelected: true, readonly: false } });
|
||||
const card = screen.getByTestId('day-meal-card');
|
||||
expect(card.getAttribute('data-selected')).toBe('true');
|
||||
});
|
||||
|
||||
it('renders empty state when slot has no recipe', () => {
|
||||
render(DayMealCard, { props: { slot: { id: 's2', slotDate: '2026-03-31', recipe: null }, isToday: false, readonly: false } });
|
||||
expect(screen.getByText(/Kein Gericht/i)).toBeTruthy();
|
||||
@@ -42,4 +54,10 @@ describe('DayMealCard', () => {
|
||||
expect(screen.getByText(/30 Min/)).toBeTruthy();
|
||||
expect(screen.getByText(/Easy/)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('empty state shows add link with suggestions href', () => {
|
||||
render(DayMealCard, { props: { slot: { id: 's2', slotDate: '2026-03-31', recipe: null }, isToday: false, readonly: false } });
|
||||
const link = screen.getByRole('link', { name: /Gericht hinzufügen/i });
|
||||
expect(link.getAttribute('href')).toContain('2026-03-31');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user