feat(gameplay): expose canonical reveal payload in frontend contract
Some checks failed
CI / test-and-quality (push) Failing after 2m8s

This commit is contained in:
2026-03-13 17:49:13 +00:00
parent b968ea4430
commit f0ebc25da7
3 changed files with 254 additions and 0 deletions

View File

@@ -17,6 +17,7 @@ describe('createAngularApiClient', () => {
{ id: 3, nickname: 'Bo', score: 0, is_connected: false }
],
round_question: null,
reveal: null,
phase_view_model: {
status: 'lobby',
round_number: 1,
@@ -84,6 +85,7 @@ describe('createAngularApiClient', () => {
if (session.ok) {
expect(session.data.session.code).toBe('ABCD12');
expect(session.data.session.host_id).toBe(1);
expect(session.data.reveal).toBeNull();
expect(session.data.phase_view_model.host.can_start_round).toBe(true);
}
@@ -119,6 +121,7 @@ describe('createAngularApiClient', () => {
session: { code: 'ABCD12', status: 'lobby', host_id: 1, current_round: 1, players_count: 2 },
players: [],
round_question: null,
reveal: null,
phase_view_model: {
status: 'lobby',
round_number: 1,
@@ -189,6 +192,117 @@ describe('createAngularApiClient', () => {
);
});
it('maps canonical reveal payload from session detail in reveal phase', async () => {
const get = vi.fn<AngularHttpClientLike['get']>(async <T>(url: string) => {
if (url === '/lobby/sessions/ABCD12') {
return {
session: { code: 'ABCD12', status: 'reveal', host_id: 1, current_round: 1, players_count: 3 },
players: [
{ id: 2, nickname: 'Maja', score: 4, is_connected: true },
{ id: 3, nickname: 'Bo', score: 2, is_connected: true },
{ id: 4, nickname: 'Ida', score: 5, is_connected: true }
],
round_question: {
id: 77,
round_number: 1,
prompt: 'Hvem opfandt pæren?',
shown_at: '2026-03-01T16:00:00Z',
answers: [{ text: 'Edison' }, { text: 'Tesla' }]
},
reveal: {
round_question_id: 77,
round_number: 1,
prompt: 'Hvem opfandt pæren?',
correct_answer: 'Edison',
lies: [
{
player_id: 3,
nickname: 'Bo',
text: 'Tesla',
created_at: '2026-03-01T16:00:20Z'
}
],
guesses: [
{
player_id: 2,
nickname: 'Maja',
selected_text: 'Tesla',
is_correct: false,
fooled_player_id: 3,
fooled_player_nickname: 'Bo',
created_at: '2026-03-01T16:01:00Z'
},
{
player_id: 4,
nickname: 'Ida',
selected_text: 'Edison',
is_correct: true,
fooled_player_id: null,
created_at: '2026-03-01T16:01:02Z'
}
]
},
phase_view_model: {
status: 'reveal',
round_number: 1,
players_count: 3,
constraints: {
min_players_to_start: 2,
max_players_mvp: 8,
min_players_reached: true,
max_players_allowed: true
},
host: {
can_start_round: false,
can_show_question: false,
can_mix_answers: false,
can_calculate_scores: false,
can_reveal_scoreboard: true,
can_start_next_round: true,
can_finish_game: true
},
player: {
can_join: false,
can_submit_lie: false,
can_submit_guess: false,
can_view_final_result: false
}
}
} as T;
}
throw { status: 404, error: { error: 'Not found' } };
});
const client = createAngularApiClient({ get, post: vi.fn() } as AngularHttpClientLike);
const session = await client.getSession('abcd12');
expect(session.ok).toBe(true);
if (session.ok) {
expect(session.data.reveal?.correct_answer).toBe('Edison');
expect(session.data.reveal?.lies[0]).toMatchObject({
player_id: 3,
nickname: 'Bo',
text: 'Tesla'
});
expect(session.data.reveal?.guesses[0]).toMatchObject({
player_id: 2,
nickname: 'Maja',
selected_text: 'Tesla',
is_correct: false,
fooled_player_id: 3,
fooled_player_nickname: 'Bo'
});
expect(session.data.reveal?.guesses[1]).toMatchObject({
player_id: 4,
nickname: 'Ida',
selected_text: 'Edison',
is_correct: true,
fooled_player_id: null
});
}
});
it('returns parse error when successful payload breaks typed contract', async () => {
const http = {
get: vi.fn<AngularHttpClientLike['get']>(async <T>() => ({ ok: true } as T)),
@@ -248,6 +362,39 @@ describe('createAngularApiClient', () => {
session: { code: 'ABCD12', status: 'reveal', current_round: 1 },
round_question: { id: 77, round_number: 1 },
events_created: 3,
reveal: {
round_question_id: 77,
round_number: 1,
prompt: 'Hvem opfandt pæren?',
correct_answer: 'Edison',
lies: [
{
player_id: 3,
nickname: 'Bo',
text: 'Tesla',
created_at: '2026-03-01T16:00:20Z'
}
],
guesses: [
{
player_id: 2,
nickname: 'Maja',
selected_text: 'Tesla',
is_correct: false,
fooled_player_id: 3,
fooled_player_nickname: 'Bo',
created_at: '2026-03-01T16:01:00Z'
},
{
player_id: 4,
nickname: 'Ida',
selected_text: 'Edison',
is_correct: true,
fooled_player_id: null,
created_at: '2026-03-01T16:01:02Z'
}
]
},
leaderboard: [{ id: 2, nickname: 'Maja', score: 11 }]
} as T;
}
@@ -305,6 +452,29 @@ describe('createAngularApiClient', () => {
const calculateScores = await client.calculateScores('abcd12', 77);
expect(calculateScores.ok).toBe(true);
if (calculateScores.ok) {
expect(calculateScores.data.reveal.correct_answer).toBe('Edison');
expect(calculateScores.data.reveal.lies[0]).toMatchObject({
player_id: 3,
nickname: 'Bo',
text: 'Tesla'
});
expect(calculateScores.data.reveal.guesses[0]).toMatchObject({
player_id: 2,
nickname: 'Maja',
selected_text: 'Tesla',
is_correct: false,
fooled_player_id: 3,
fooled_player_nickname: 'Bo'
});
expect(calculateScores.data.reveal.guesses[1]).toMatchObject({
player_id: 4,
nickname: 'Ida',
selected_text: 'Edison',
is_correct: true,
fooled_player_id: null
});
}
const scoreboard = await client.getScoreboard('abcd12');
expect(scoreboard.ok).toBe(true);