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:
2026-04-03 11:07:47 +02:00
parent e3f8d8ad73
commit 5d2bb9e84e
9 changed files with 336 additions and 59 deletions

View File

@@ -111,7 +111,8 @@ describe('planner page — actions', () => {
formData.set('weekStart', '2026-03-30');
const result = await actions.createPlan({
fetch: vi.fn(),
request: { formData: async () => formData }
request: { formData: async () => formData },
locals: { benutzer: { id: 'u1', name: 'Test', rolle: 'planer' }, haushalt: { id: 'h1', name: 'Test' } }
});
expect(mockPost).toHaveBeenCalledWith('/v1/week-plans', expect.objectContaining({ body: { weekStart: '2026-03-30' } }));
expect(result).toEqual({ success: true });
@@ -123,8 +124,72 @@ describe('planner page — actions', () => {
formData.set('weekStart', '2026-03-30');
const result = await actions.createPlan({
fetch: vi.fn(),
request: { formData: async () => formData }
request: { formData: async () => formData },
locals: { benutzer: { id: 'u1', name: 'Test', rolle: 'planer' }, haushalt: { id: 'h1', name: 'Test' } }
});
expect(result).toEqual({ success: false, error: expect.any(String) });
});
it('createPlan action returns error for invalid weekStart format', async () => {
const formData = new FormData();
formData.set('weekStart', 'not-a-date');
const result = await actions.createPlan({
fetch: vi.fn(),
request: { formData: async () => formData },
locals: { benutzer: { id: 'u1', name: 'Test', rolle: 'planer' }, haushalt: { id: 'h1', name: 'Test' } }
});
expect(result).toEqual({ success: false, error: 'Ungültiges Datum.' });
expect(mockPost).not.toHaveBeenCalled();
});
it('createPlan action returns error when weekStart is missing', async () => {
const formData = new FormData();
const result = await actions.createPlan({
fetch: vi.fn(),
request: { formData: async () => formData },
locals: { benutzer: { id: 'u1', name: 'Test', rolle: 'planer' }, haushalt: { id: 'h1', name: 'Test' } }
});
expect(result).toEqual({ success: false, error: 'Ungültiges Datum.' });
});
it('createPlan action returns permission error for member role', async () => {
const formData = new FormData();
formData.set('weekStart', '2026-03-30');
const result = await actions.createPlan({
fetch: vi.fn(),
request: { formData: async () => formData },
locals: { benutzer: { id: 'u1', name: 'Test', rolle: 'mitglied' }, haushalt: { id: 'h1', name: 'Test' } }
});
expect(result).toEqual({ success: false, error: 'Keine Berechtigung.' });
expect(mockPost).not.toHaveBeenCalled();
});
});
describe('planner page — variety score partial failure', () => {
let load: any;
beforeEach(async () => {
mockGet.mockReset();
mockPost.mockReset();
vi.resetModules();
const mod = await import('./+page.server');
load = mod.load;
});
const mockWeekPlan = {
id: 'plan-1',
weekStart: '2026-03-30',
status: 'draft',
slots: []
};
it('returns weekPlan even when variety score API fails', async () => {
mockGet.mockResolvedValueOnce({ data: mockWeekPlan, error: undefined });
mockGet.mockResolvedValueOnce({ data: undefined, error: { status: 500 } });
const url = new URL('http://localhost/planner');
const result = await load({ fetch: vi.fn(), url });
expect(result.weekPlan).toBeDefined();
expect(result.weekPlan.id).toBe('plan-1');
expect(result.varietyScore).toBeNull();
});
});