import { describe, expect, it, vi } from 'vitest'; import { createVerticalSliceController } from '../src/spa/vertical-slice'; import type { ApiClient } from '../src/api/client'; function makeApiMock(overrides?: Partial): ApiClient { const base: ApiClient = { health: vi.fn(), getSession: vi.fn().mockResolvedValue({ ok: true, status: 200, data: { session: { code: 'ABCD12', status: 'lobby', host_id: 1, current_round: 1, players_count: 3 }, players: [], round_question: null, phase_view_model: { status: 'lobby', round_number: 1, players_count: 3, constraints: { min_players_to_start: 3, max_players_mvp: 5, min_players_reached: true, max_players_allowed: true }, host: { can_start_round: true, can_show_question: false, can_mix_answers: false, can_calculate_scores: false, can_reveal_scoreboard: false, can_start_next_round: false, can_finish_game: false }, player: { can_join: true, can_submit_lie: false, can_submit_guess: false, can_view_final_result: false } } } }), joinSession: vi.fn().mockResolvedValue({ ok: true, status: 201, data: { player: { id: 9, nickname: 'Maja', session_token: 'token-1', score: 0 }, session: { code: 'ABCD12', status: 'lobby' } } }), startRound: vi.fn().mockResolvedValue({ ok: true, status: 201, data: { session: { code: 'ABCD12', status: 'lie', current_round: 1 }, round: { number: 1, category: { slug: 'history', name: 'History' } } } }) }; return { ...base, ...overrides }; } describe('vertical slice controller: lobby -> join -> start round', () => { it('tracks loading and success state for join + start flow', async () => { const api = makeApiMock(); const controller = createVerticalSliceController(api); const beforeJoinPromise = controller.joinLobby('abcd12', 'Maja'); expect(controller.getState().joinState).toBe('loading'); await beforeJoinPromise; const postJoin = controller.getState(); expect(postJoin.joinState).toBe('success'); expect(postJoin.session?.session.code).toBe('ABCD12'); const beforeStartPromise = controller.startRound('abcd12', 'history'); expect(controller.getState().startRoundState).toBe('loading'); await beforeStartPromise; const postStart = controller.getState(); expect(postStart.startRoundState).toBe('success'); }); it('surfaces a friendly error when join fails', async () => { const api = makeApiMock({ joinSession: vi.fn().mockResolvedValue({ ok: false, status: 404, error: { kind: 'http', status: 404, message: 'HTTP 404', payload: { error: 'Session not found' } } }) }); const controller = createVerticalSliceController(api); await controller.joinLobby('missing', 'Maja'); const state = controller.getState(); expect(state.joinState).toBe('error'); expect(state.errorMessage).toContain('Join fejlede'); }); it('surfaces a friendly error when round start fails', async () => { const api = makeApiMock({ startRound: vi.fn().mockResolvedValue({ ok: false, status: 400, error: { kind: 'http', status: 400, message: 'HTTP 400', payload: { error: 'Round can only be started from lobby' } } }) }); const controller = createVerticalSliceController(api); await controller.startRound('ABCD12', 'history'); const state = controller.getState(); expect(state.startRoundState).toBe('error'); expect(state.errorMessage).toContain('Kunne ikke starte runden'); }); });