204 lines
6.8 KiB
TypeScript
204 lines
6.8 KiB
TypeScript
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
|
|
import { HostShellComponent } from './host-shell.component';
|
|
|
|
type FetchMock = ReturnType<typeof vi.fn>;
|
|
|
|
function jsonResponse(status: number, body: unknown) {
|
|
return {
|
|
ok: status >= 200 && status < 300,
|
|
status,
|
|
json: vi.fn().mockResolvedValue(body),
|
|
} as unknown as Response;
|
|
}
|
|
|
|
describe('HostShellComponent gameplay wiring', () => {
|
|
afterEach(() => {
|
|
vi.restoreAllMocks();
|
|
});
|
|
|
|
it('runs startRound transition and refreshes session details', async () => {
|
|
const fetchMock: FetchMock = vi
|
|
.fn()
|
|
.mockResolvedValueOnce(jsonResponse(201, { ok: true }))
|
|
.mockResolvedValueOnce(
|
|
jsonResponse(200, {
|
|
session: { code: 'ABCD12', status: 'lie', current_round: 2 },
|
|
round_question: { id: 41, prompt: 'Q?', answers: [] },
|
|
players: [],
|
|
})
|
|
);
|
|
|
|
vi.stubGlobal('fetch', fetchMock);
|
|
|
|
const component = new HostShellComponent();
|
|
component.sessionCode = ' abcd12 ';
|
|
component.categorySlug = ' history ';
|
|
|
|
await component.startRound();
|
|
|
|
expect(fetchMock).toHaveBeenNthCalledWith(
|
|
1,
|
|
'/lobby/sessions/ABCD12/rounds/start',
|
|
expect.objectContaining({ method: 'POST', body: JSON.stringify({ category_slug: 'history' }) })
|
|
);
|
|
expect(fetchMock).toHaveBeenNthCalledWith(
|
|
2,
|
|
'/lobby/sessions/ABCD12',
|
|
expect.objectContaining({ method: 'GET' })
|
|
);
|
|
expect(component.session?.session.status).toBe('lie');
|
|
expect(component.roundQuestionId).toBe('41');
|
|
expect(component.loading).toBe(false);
|
|
});
|
|
|
|
it('captures scoreboard error for retry path', async () => {
|
|
const fetchMock: FetchMock = vi.fn().mockResolvedValue(
|
|
jsonResponse(500, { error: 'Scoreboard unavailable' })
|
|
);
|
|
|
|
vi.stubGlobal('fetch', fetchMock);
|
|
|
|
const component = new HostShellComponent();
|
|
component.sessionCode = 'ABCD12';
|
|
|
|
await component.loadScoreboard();
|
|
|
|
expect(fetchMock).toHaveBeenCalledWith(
|
|
'/lobby/sessions/ABCD12/scoreboard',
|
|
expect.objectContaining({ method: 'GET' })
|
|
);
|
|
expect(component.scoreboardError).toContain('Scoreboard failed: Scoreboard unavailable');
|
|
expect(component.loading).toBe(false);
|
|
});
|
|
|
|
it('wires showQuestion, mixAnswers and calculateScores with expected request payloads', async () => {
|
|
const sessionAfterAction = {
|
|
session: { code: 'ABCD12', status: 'guess', current_round: 1 },
|
|
round_question: { id: 77, prompt: 'Q?', answers: [] },
|
|
players: [],
|
|
};
|
|
|
|
const fetchMock: FetchMock = vi
|
|
.fn()
|
|
.mockResolvedValueOnce(jsonResponse(200, { ok: true }))
|
|
.mockResolvedValueOnce(jsonResponse(200, sessionAfterAction))
|
|
.mockResolvedValueOnce(jsonResponse(200, { ok: true }))
|
|
.mockResolvedValueOnce(jsonResponse(200, sessionAfterAction))
|
|
.mockResolvedValueOnce(jsonResponse(200, { ok: true }))
|
|
.mockResolvedValueOnce(jsonResponse(200, sessionAfterAction));
|
|
|
|
vi.stubGlobal('fetch', fetchMock);
|
|
|
|
const component = new HostShellComponent();
|
|
component.sessionCode = ' abcd12 ';
|
|
component.roundQuestionId = ' 77 ';
|
|
|
|
await component.showQuestion();
|
|
await component.mixAnswers();
|
|
await component.calculateScores();
|
|
|
|
expect(fetchMock).toHaveBeenNthCalledWith(
|
|
1,
|
|
'/lobby/sessions/ABCD12/questions/show',
|
|
expect.objectContaining({ method: 'POST', body: JSON.stringify({}) })
|
|
);
|
|
expect(fetchMock).toHaveBeenNthCalledWith(
|
|
3,
|
|
'/lobby/sessions/ABCD12/questions/77/answers/mix',
|
|
expect.objectContaining({ method: 'POST', body: JSON.stringify({}) })
|
|
);
|
|
expect(fetchMock).toHaveBeenNthCalledWith(
|
|
5,
|
|
'/lobby/sessions/ABCD12/questions/77/scores/calculate',
|
|
expect.objectContaining({ method: 'POST', body: JSON.stringify({}) })
|
|
);
|
|
|
|
expect(component.error).toBe('');
|
|
expect(component.loading).toBe(false);
|
|
});
|
|
|
|
it('runs next-round transition without reload and clears scoreboard payload', async () => {
|
|
const fetchMock: FetchMock = vi
|
|
.fn()
|
|
.mockResolvedValueOnce(jsonResponse(200, { session: { code: 'ABCD12', status: 'lobby', current_round: 2 } }))
|
|
.mockResolvedValueOnce(
|
|
jsonResponse(200, {
|
|
session: { code: 'ABCD12', status: 'lobby', current_round: 2 },
|
|
round_question: null,
|
|
players: [],
|
|
})
|
|
);
|
|
|
|
vi.stubGlobal('fetch', fetchMock);
|
|
|
|
const component = new HostShellComponent();
|
|
component.sessionCode = ' abcd12 ';
|
|
component.scoreboardPayload = '{"leaderboard":[]}';
|
|
component.finalLeaderboardPayload = '{"leaderboard":[{"nickname":"Old","score":1}]}';
|
|
component.finalLeaderboard = [{ id: 9, nickname: 'Old', score: 1 }];
|
|
|
|
await component.startNextRound();
|
|
|
|
expect(fetchMock).toHaveBeenNthCalledWith(
|
|
1,
|
|
'/lobby/sessions/ABCD12/rounds/next',
|
|
expect.objectContaining({ method: 'POST', body: JSON.stringify({}) })
|
|
);
|
|
expect(fetchMock).toHaveBeenNthCalledWith(
|
|
2,
|
|
'/lobby/sessions/ABCD12',
|
|
expect.objectContaining({ method: 'GET' })
|
|
);
|
|
expect(component.session?.session.status).toBe('lobby');
|
|
expect(component.scoreboardPayload).toBe('');
|
|
expect(component.finalLeaderboardPayload).toBe('');
|
|
expect(component.finalLeaderboard).toEqual([]);
|
|
expect(component.nextRoundError).toBe('');
|
|
});
|
|
|
|
it('captures finish-game failure for retry and stores final leaderboard on success', async () => {
|
|
const fetchMock: FetchMock = vi
|
|
.fn()
|
|
.mockResolvedValueOnce(jsonResponse(503, { error: 'Final leaderboard timeout' }))
|
|
.mockResolvedValueOnce(
|
|
jsonResponse(200, {
|
|
session: { code: 'ABCD12', status: 'finished', current_round: 2 },
|
|
winner: { id: 1, nickname: 'Luna', score: 320 },
|
|
leaderboard: [
|
|
{ id: 2, nickname: 'Mads', score: 120 },
|
|
{ id: 1, nickname: 'Luna', score: 320 },
|
|
],
|
|
})
|
|
)
|
|
.mockResolvedValueOnce(
|
|
jsonResponse(200, {
|
|
session: { code: 'ABCD12', status: 'finished', current_round: 2 },
|
|
round_question: null,
|
|
players: [],
|
|
})
|
|
);
|
|
|
|
vi.stubGlobal('fetch', fetchMock);
|
|
|
|
const component = new HostShellComponent();
|
|
component.sessionCode = 'ABCD12';
|
|
|
|
await component.finishGame();
|
|
|
|
expect(component.finishError).toContain('Finish game failed: Final leaderboard timeout');
|
|
|
|
await component.finishGame();
|
|
|
|
expect(fetchMock).toHaveBeenNthCalledWith(
|
|
2,
|
|
'/lobby/sessions/ABCD12/finish',
|
|
expect.objectContaining({ method: 'POST', body: JSON.stringify({}) })
|
|
);
|
|
expect(component.finishError).toBe('');
|
|
expect(component.finalLeaderboardPayload).toContain('\"status\": \"finished\"');
|
|
expect(component.finalWinner?.nickname).toBe('Luna');
|
|
expect(component.finalLeaderboard.map((entry) => entry.nickname)).toEqual(['Luna', 'Mads']);
|
|
});
|
|
});
|