[READY][SPA][P9] Angular API-contract guard: typed client + response mappers for host/player flow #196
@@ -1,18 +1,36 @@
|
||||
import {
|
||||
mapCalculateScoresResponse,
|
||||
mapFinishGameResponse,
|
||||
mapHealthResponse,
|
||||
mapJoinSessionResponse,
|
||||
mapMixAnswersResponse,
|
||||
mapScoreboardResponse,
|
||||
mapSessionDetailResponse,
|
||||
mapStartRoundResponse
|
||||
mapShowQuestionResponse,
|
||||
mapStartNextRoundResponse,
|
||||
mapStartRoundResponse,
|
||||
mapSubmitGuessResponse,
|
||||
mapSubmitLieResponse
|
||||
} from './mappers';
|
||||
import type {
|
||||
ApiFailure,
|
||||
ApiResult,
|
||||
CalculateScoresResponse,
|
||||
FinishGameResponse,
|
||||
HealthResponse,
|
||||
JoinSessionRequest,
|
||||
JoinSessionResponse,
|
||||
MixAnswersResponse,
|
||||
ScoreboardResponse,
|
||||
SessionDetailResponse,
|
||||
ShowQuestionResponse,
|
||||
StartNextRoundResponse,
|
||||
StartRoundRequest,
|
||||
StartRoundResponse
|
||||
StartRoundResponse,
|
||||
SubmitGuessRequest,
|
||||
SubmitGuessResponse,
|
||||
SubmitLieRequest,
|
||||
SubmitLieResponse
|
||||
} from './types';
|
||||
|
||||
export interface AngularHttpError {
|
||||
@@ -31,6 +49,18 @@ export interface AngularApiClient {
|
||||
getSession(code: string): Promise<ApiResult<SessionDetailResponse>>;
|
||||
joinSession(payload: JoinSessionRequest): Promise<ApiResult<JoinSessionResponse>>;
|
||||
startRound(code: string, payload: StartRoundRequest): Promise<ApiResult<StartRoundResponse>>;
|
||||
showQuestion(code: string): Promise<ApiResult<ShowQuestionResponse>>;
|
||||
mixAnswers(code: string, roundQuestionId: number): Promise<ApiResult<MixAnswersResponse>>;
|
||||
calculateScores(code: string, roundQuestionId: number): Promise<ApiResult<CalculateScoresResponse>>;
|
||||
getScoreboard(code: string): Promise<ApiResult<ScoreboardResponse>>;
|
||||
startNextRound(code: string): Promise<ApiResult<StartNextRoundResponse>>;
|
||||
finishGame(code: string): Promise<ApiResult<FinishGameResponse>>;
|
||||
submitLie(code: string, roundQuestionId: number, payload: SubmitLieRequest): Promise<ApiResult<SubmitLieResponse>>;
|
||||
submitGuess(
|
||||
code: string,
|
||||
roundQuestionId: number,
|
||||
payload: SubmitGuessRequest
|
||||
): Promise<ApiResult<SubmitGuessResponse>>;
|
||||
}
|
||||
|
||||
function toFailure(error: unknown): ApiFailure {
|
||||
@@ -128,6 +158,105 @@ export function createAngularApiClient(http: AngularHttpClientLike, baseUrl = ''
|
||||
{ withCredentials: true }
|
||||
),
|
||||
mapStartRoundResponse
|
||||
),
|
||||
showQuestion: (code: string) =>
|
||||
wrap(
|
||||
() =>
|
||||
http.post<ShowQuestionResponse>(
|
||||
buildUrl(baseUrl, `/lobby/sessions/${encodeURIComponent(normalizeCode(code))}/questions/show`),
|
||||
{},
|
||||
{ withCredentials: true }
|
||||
),
|
||||
mapShowQuestionResponse
|
||||
),
|
||||
mixAnswers: (code: string, roundQuestionId: number) =>
|
||||
wrap(
|
||||
() =>
|
||||
http.post<MixAnswersResponse>(
|
||||
buildUrl(
|
||||
baseUrl,
|
||||
`/lobby/sessions/${encodeURIComponent(normalizeCode(code))}/questions/${roundQuestionId}/answers/mix`
|
||||
),
|
||||
{},
|
||||
{ withCredentials: true }
|
||||
),
|
||||
mapMixAnswersResponse
|
||||
),
|
||||
calculateScores: (code: string, roundQuestionId: number) =>
|
||||
wrap(
|
||||
() =>
|
||||
http.post<CalculateScoresResponse>(
|
||||
buildUrl(
|
||||
baseUrl,
|
||||
`/lobby/sessions/${encodeURIComponent(normalizeCode(code))}/questions/${roundQuestionId}/scores/calculate`
|
||||
),
|
||||
{},
|
||||
{ withCredentials: true }
|
||||
),
|
||||
mapCalculateScoresResponse
|
||||
),
|
||||
getScoreboard: (code: string) =>
|
||||
wrap(
|
||||
() =>
|
||||
http.get<ScoreboardResponse>(
|
||||
buildUrl(baseUrl, `/lobby/sessions/${encodeURIComponent(normalizeCode(code))}/scoreboard`),
|
||||
{ withCredentials: true }
|
||||
),
|
||||
mapScoreboardResponse
|
||||
),
|
||||
startNextRound: (code: string) =>
|
||||
wrap(
|
||||
() =>
|
||||
http.post<StartNextRoundResponse>(
|
||||
buildUrl(baseUrl, `/lobby/sessions/${encodeURIComponent(normalizeCode(code))}/rounds/next`),
|
||||
{},
|
||||
{ withCredentials: true }
|
||||
),
|
||||
mapStartNextRoundResponse
|
||||
),
|
||||
finishGame: (code: string) =>
|
||||
wrap(
|
||||
() =>
|
||||
http.post<FinishGameResponse>(
|
||||
buildUrl(baseUrl, `/lobby/sessions/${encodeURIComponent(normalizeCode(code))}/finish`),
|
||||
{},
|
||||
{ withCredentials: true }
|
||||
),
|
||||
mapFinishGameResponse
|
||||
),
|
||||
submitLie: (code: string, roundQuestionId: number, payload: SubmitLieRequest) =>
|
||||
wrap(
|
||||
() =>
|
||||
http.post<SubmitLieResponse>(
|
||||
buildUrl(
|
||||
baseUrl,
|
||||
`/lobby/sessions/${encodeURIComponent(normalizeCode(code))}/questions/${roundQuestionId}/lies/submit`
|
||||
),
|
||||
{
|
||||
player_id: payload.player_id,
|
||||
session_token: payload.session_token,
|
||||
text: payload.text
|
||||
},
|
||||
{ withCredentials: true }
|
||||
),
|
||||
mapSubmitLieResponse
|
||||
),
|
||||
submitGuess: (code: string, roundQuestionId: number, payload: SubmitGuessRequest) =>
|
||||
wrap(
|
||||
() =>
|
||||
http.post<SubmitGuessResponse>(
|
||||
buildUrl(
|
||||
baseUrl,
|
||||
`/lobby/sessions/${encodeURIComponent(normalizeCode(code))}/questions/${roundQuestionId}/guesses/submit`
|
||||
),
|
||||
{
|
||||
player_id: payload.player_id,
|
||||
session_token: payload.session_token,
|
||||
selected_text: payload.selected_text
|
||||
},
|
||||
{ withCredentials: true }
|
||||
),
|
||||
mapSubmitGuessResponse
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,4 +1,17 @@
|
||||
import type { HealthResponse, JoinSessionResponse, SessionDetailResponse, StartRoundResponse } from './types';
|
||||
import type {
|
||||
CalculateScoresResponse,
|
||||
FinishGameResponse,
|
||||
HealthResponse,
|
||||
JoinSessionResponse,
|
||||
MixAnswersResponse,
|
||||
ScoreboardResponse,
|
||||
SessionDetailResponse,
|
||||
ShowQuestionResponse,
|
||||
StartNextRoundResponse,
|
||||
StartRoundResponse,
|
||||
SubmitGuessResponse,
|
||||
SubmitLieResponse
|
||||
} from './types';
|
||||
|
||||
function isRecord(value: unknown): value is Record<string, unknown> {
|
||||
return typeof value === 'object' && value !== null;
|
||||
@@ -189,3 +202,158 @@ export function mapStartRoundResponse(payload: unknown): StartRoundResponse {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function mapLeaderboardEntry(payload: unknown, path: string): { id: number; nickname: string; score: number } {
|
||||
const record = asRecord(payload, path);
|
||||
return {
|
||||
id: readNumber(record, 'id', path),
|
||||
nickname: readString(record, 'nickname', path),
|
||||
score: readNumber(record, 'score', path)
|
||||
};
|
||||
}
|
||||
|
||||
function mapSessionState(payload: unknown, path: string): { code: string; status: string; current_round: number } {
|
||||
const session = asRecord(payload, path);
|
||||
return {
|
||||
code: readString(session, 'code', path),
|
||||
status: readString(session, 'status', path),
|
||||
current_round: readNumber(session, 'current_round', path)
|
||||
};
|
||||
}
|
||||
|
||||
export function mapShowQuestionResponse(payload: unknown): ShowQuestionResponse {
|
||||
const root = asRecord(payload, 'show_question');
|
||||
const roundQuestion = asRecord(root.round_question, 'show_question.round_question');
|
||||
const config = asRecord(root.config, 'show_question.config');
|
||||
|
||||
return {
|
||||
round_question: {
|
||||
id: readNumber(roundQuestion, 'id', 'show_question.round_question'),
|
||||
prompt: readString(roundQuestion, 'prompt', 'show_question.round_question'),
|
||||
round_number: readNumber(roundQuestion, 'round_number', 'show_question.round_question'),
|
||||
shown_at: readString(roundQuestion, 'shown_at', 'show_question.round_question'),
|
||||
lie_deadline_at: readString(roundQuestion, 'lie_deadline_at', 'show_question.round_question')
|
||||
},
|
||||
config: {
|
||||
lie_seconds: readNumber(config, 'lie_seconds', 'show_question.config')
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function mapMixAnswersResponse(payload: unknown): MixAnswersResponse {
|
||||
const root = asRecord(payload, 'mix_answers');
|
||||
const roundQuestion = asRecord(root.round_question, 'mix_answers.round_question');
|
||||
const answersRaw = root.answers;
|
||||
if (!Array.isArray(answersRaw)) {
|
||||
throw new Error('Invalid API contract: expected array at mix_answers.answers');
|
||||
}
|
||||
|
||||
return {
|
||||
session: mapSessionState(root.session, 'mix_answers.session'),
|
||||
round_question: {
|
||||
id: readNumber(roundQuestion, 'id', 'mix_answers.round_question'),
|
||||
round_number: readNumber(roundQuestion, 'round_number', 'mix_answers.round_question')
|
||||
},
|
||||
answers: answersRaw.map((answer, index) => {
|
||||
const record = asRecord(answer, `mix_answers.answers[${index}]`);
|
||||
return { text: readString(record, 'text', `mix_answers.answers[${index}]`) };
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
export function mapCalculateScoresResponse(payload: unknown): CalculateScoresResponse {
|
||||
const root = asRecord(payload, 'calculate_scores');
|
||||
const roundQuestion = asRecord(root.round_question, 'calculate_scores.round_question');
|
||||
const leaderboardRaw = root.leaderboard;
|
||||
if (!Array.isArray(leaderboardRaw)) {
|
||||
throw new Error('Invalid API contract: expected array at calculate_scores.leaderboard');
|
||||
}
|
||||
|
||||
return {
|
||||
session: mapSessionState(root.session, 'calculate_scores.session'),
|
||||
round_question: {
|
||||
id: readNumber(roundQuestion, 'id', 'calculate_scores.round_question'),
|
||||
round_number: readNumber(roundQuestion, 'round_number', 'calculate_scores.round_question')
|
||||
},
|
||||
events_created: readNumber(root, 'events_created', 'calculate_scores'),
|
||||
leaderboard: leaderboardRaw.map((entry, index) => mapLeaderboardEntry(entry, `calculate_scores.leaderboard[${index}]`))
|
||||
};
|
||||
}
|
||||
|
||||
export function mapScoreboardResponse(payload: unknown): ScoreboardResponse {
|
||||
const root = asRecord(payload, 'scoreboard');
|
||||
const leaderboardRaw = root.leaderboard;
|
||||
if (!Array.isArray(leaderboardRaw)) {
|
||||
throw new Error('Invalid API contract: expected array at scoreboard.leaderboard');
|
||||
}
|
||||
|
||||
return {
|
||||
session: mapSessionState(root.session, 'scoreboard.session'),
|
||||
leaderboard: leaderboardRaw.map((entry, index) => mapLeaderboardEntry(entry, `scoreboard.leaderboard[${index}]`))
|
||||
};
|
||||
}
|
||||
|
||||
export function mapStartNextRoundResponse(payload: unknown): StartNextRoundResponse {
|
||||
const root = asRecord(payload, 'start_next_round');
|
||||
return { session: mapSessionState(root.session, 'start_next_round.session') };
|
||||
}
|
||||
|
||||
export function mapFinishGameResponse(payload: unknown): FinishGameResponse {
|
||||
const root = asRecord(payload, 'finish_game');
|
||||
const leaderboardRaw = root.leaderboard;
|
||||
if (!Array.isArray(leaderboardRaw)) {
|
||||
throw new Error('Invalid API contract: expected array at finish_game.leaderboard');
|
||||
}
|
||||
|
||||
const winnerRaw = root.winner;
|
||||
|
||||
return {
|
||||
session: mapSessionState(root.session, 'finish_game.session'),
|
||||
winner: winnerRaw === null ? null : mapLeaderboardEntry(winnerRaw, 'finish_game.winner'),
|
||||
leaderboard: leaderboardRaw.map((entry, index) => mapLeaderboardEntry(entry, `finish_game.leaderboard[${index}]`))
|
||||
};
|
||||
}
|
||||
|
||||
export function mapSubmitLieResponse(payload: unknown): SubmitLieResponse {
|
||||
const root = asRecord(payload, 'submit_lie');
|
||||
const lie = asRecord(root.lie, 'submit_lie.lie');
|
||||
const window = asRecord(root.window, 'submit_lie.window');
|
||||
|
||||
return {
|
||||
lie: {
|
||||
id: readNumber(lie, 'id', 'submit_lie.lie'),
|
||||
player_id: readNumber(lie, 'player_id', 'submit_lie.lie'),
|
||||
round_question_id: readNumber(lie, 'round_question_id', 'submit_lie.lie'),
|
||||
text: readString(lie, 'text', 'submit_lie.lie'),
|
||||
created_at: readString(lie, 'created_at', 'submit_lie.lie')
|
||||
},
|
||||
window: {
|
||||
lie_deadline_at: readString(window, 'lie_deadline_at', 'submit_lie.window')
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function mapSubmitGuessResponse(payload: unknown): SubmitGuessResponse {
|
||||
const root = asRecord(payload, 'submit_guess');
|
||||
const guess = asRecord(root.guess, 'submit_guess.guess');
|
||||
const window = asRecord(root.window, 'submit_guess.window');
|
||||
const fooledPlayerId = guess.fooled_player_id;
|
||||
if (fooledPlayerId !== null && !isNumber(fooledPlayerId)) {
|
||||
throw new Error('Invalid API contract: expected number|null at submit_guess.guess.fooled_player_id');
|
||||
}
|
||||
|
||||
return {
|
||||
guess: {
|
||||
id: readNumber(guess, 'id', 'submit_guess.guess'),
|
||||
player_id: readNumber(guess, 'player_id', 'submit_guess.guess'),
|
||||
round_question_id: readNumber(guess, 'round_question_id', 'submit_guess.guess'),
|
||||
selected_text: readString(guess, 'selected_text', 'submit_guess.guess'),
|
||||
is_correct: readBoolean(guess, 'is_correct', 'submit_guess.guess'),
|
||||
fooled_player_id: fooledPlayerId,
|
||||
created_at: readString(guess, 'created_at', 'submit_guess.guess')
|
||||
},
|
||||
window: {
|
||||
guess_deadline_at: readString(window, 'guess_deadline_at', 'submit_guess.window')
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -101,6 +101,113 @@ export interface StartRoundResponse {
|
||||
};
|
||||
}
|
||||
|
||||
export interface ShowQuestionResponse {
|
||||
round_question: {
|
||||
id: number;
|
||||
prompt: string;
|
||||
round_number: number;
|
||||
shown_at: string;
|
||||
lie_deadline_at: string;
|
||||
};
|
||||
config: {
|
||||
lie_seconds: number;
|
||||
};
|
||||
}
|
||||
|
||||
export interface MixAnswersResponse {
|
||||
session: {
|
||||
code: string;
|
||||
status: string;
|
||||
current_round: number;
|
||||
};
|
||||
round_question: {
|
||||
id: number;
|
||||
round_number: number;
|
||||
};
|
||||
answers: Array<{ text: string }>;
|
||||
}
|
||||
|
||||
export interface CalculateScoresResponse {
|
||||
session: {
|
||||
code: string;
|
||||
status: string;
|
||||
current_round: number;
|
||||
};
|
||||
round_question: {
|
||||
id: number;
|
||||
round_number: number;
|
||||
};
|
||||
events_created: number;
|
||||
leaderboard: Array<{ id: number; nickname: string; score: number }>;
|
||||
}
|
||||
|
||||
export interface ScoreboardResponse {
|
||||
session: {
|
||||
code: string;
|
||||
status: string;
|
||||
current_round: number;
|
||||
};
|
||||
leaderboard: Array<{ id: number; nickname: string; score: number }>;
|
||||
}
|
||||
|
||||
export interface StartNextRoundResponse {
|
||||
session: {
|
||||
code: string;
|
||||
status: string;
|
||||
current_round: number;
|
||||
};
|
||||
}
|
||||
|
||||
export interface FinishGameResponse {
|
||||
session: {
|
||||
code: string;
|
||||
status: string;
|
||||
current_round: number;
|
||||
};
|
||||
winner: { id: number; nickname: string; score: number } | null;
|
||||
leaderboard: Array<{ id: number; nickname: string; score: number }>;
|
||||
}
|
||||
|
||||
export interface SubmitLieRequest {
|
||||
player_id: number;
|
||||
session_token: string;
|
||||
text: string;
|
||||
}
|
||||
|
||||
export interface SubmitLieResponse {
|
||||
lie: {
|
||||
id: number;
|
||||
player_id: number;
|
||||
round_question_id: number;
|
||||
text: string;
|
||||
created_at: string;
|
||||
};
|
||||
window: {
|
||||
lie_deadline_at: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface SubmitGuessRequest {
|
||||
player_id: number;
|
||||
session_token: string;
|
||||
selected_text: string;
|
||||
}
|
||||
|
||||
export interface SubmitGuessResponse {
|
||||
guess: {
|
||||
id: number;
|
||||
player_id: number;
|
||||
round_question_id: number;
|
||||
selected_text: string;
|
||||
is_correct: boolean;
|
||||
fooled_player_id: number | null;
|
||||
created_at: string;
|
||||
};
|
||||
window: {
|
||||
guess_deadline_at: string;
|
||||
};
|
||||
}
|
||||
|
||||
export type ApiErrorKind = 'network' | 'http' | 'parse';
|
||||
|
||||
export interface ApiFailure {
|
||||
|
||||
@@ -206,6 +206,126 @@ describe('createAngularApiClient', () => {
|
||||
}
|
||||
});
|
||||
|
||||
it('maps host/player gameplay endpoints through typed response mappers', async () => {
|
||||
const get = vi.fn<AngularHttpClientLike['get']>(async <T>(url: string) => {
|
||||
if (url === '/lobby/sessions/ABCD12/scoreboard') {
|
||||
return {
|
||||
session: { code: 'ABCD12', status: 'reveal', current_round: 1 },
|
||||
leaderboard: [
|
||||
{ id: 2, nickname: 'Maja', score: 11 },
|
||||
{ id: 3, nickname: 'Bo', score: 7 }
|
||||
]
|
||||
} as T;
|
||||
}
|
||||
throw { status: 404, error: { error: 'Not found' } };
|
||||
});
|
||||
|
||||
const post = vi.fn<AngularHttpClientLike['post']>(async <T>(url: string, body: unknown) => {
|
||||
if (url === '/lobby/sessions/ABCD12/questions/show') {
|
||||
expect(body).toEqual({});
|
||||
return {
|
||||
round_question: {
|
||||
id: 77,
|
||||
prompt: 'Prompt?',
|
||||
round_number: 1,
|
||||
shown_at: '2026-03-01T16:00:00Z',
|
||||
lie_deadline_at: '2026-03-01T16:00:30Z'
|
||||
},
|
||||
config: { lie_seconds: 30 }
|
||||
} as T;
|
||||
}
|
||||
if (url === '/lobby/sessions/ABCD12/questions/77/answers/mix') {
|
||||
expect(body).toEqual({});
|
||||
return {
|
||||
session: { code: 'ABCD12', status: 'guess', current_round: 1 },
|
||||
round_question: { id: 77, round_number: 1 },
|
||||
answers: [{ text: 'A' }, { text: 'B' }]
|
||||
} as T;
|
||||
}
|
||||
if (url === '/lobby/sessions/ABCD12/questions/77/scores/calculate') {
|
||||
expect(body).toEqual({});
|
||||
return {
|
||||
session: { code: 'ABCD12', status: 'reveal', current_round: 1 },
|
||||
round_question: { id: 77, round_number: 1 },
|
||||
events_created: 3,
|
||||
leaderboard: [{ id: 2, nickname: 'Maja', score: 11 }]
|
||||
} as T;
|
||||
}
|
||||
if (url === '/lobby/sessions/ABCD12/rounds/next') {
|
||||
expect(body).toEqual({});
|
||||
return { session: { code: 'ABCD12', status: 'lobby', current_round: 2 } } as T;
|
||||
}
|
||||
if (url === '/lobby/sessions/ABCD12/finish') {
|
||||
expect(body).toEqual({});
|
||||
return {
|
||||
session: { code: 'ABCD12', status: 'finished', current_round: 2 },
|
||||
winner: { id: 2, nickname: 'Maja', score: 15 },
|
||||
leaderboard: [{ id: 2, nickname: 'Maja', score: 15 }]
|
||||
} as T;
|
||||
}
|
||||
if (url === '/lobby/sessions/ABCD12/questions/77/lies/submit') {
|
||||
expect(body).toEqual({ player_id: 9, session_token: 'tok', text: 'my lie' });
|
||||
return {
|
||||
lie: {
|
||||
id: 100,
|
||||
player_id: 9,
|
||||
round_question_id: 77,
|
||||
text: 'my lie',
|
||||
created_at: '2026-03-01T16:00:10Z'
|
||||
},
|
||||
window: { lie_deadline_at: '2026-03-01T16:00:30Z' }
|
||||
} as T;
|
||||
}
|
||||
if (url === '/lobby/sessions/ABCD12/questions/77/guesses/submit') {
|
||||
expect(body).toEqual({ player_id: 9, session_token: 'tok', selected_text: 'A' });
|
||||
return {
|
||||
guess: {
|
||||
id: 200,
|
||||
player_id: 9,
|
||||
round_question_id: 77,
|
||||
selected_text: 'A',
|
||||
is_correct: false,
|
||||
fooled_player_id: 3,
|
||||
created_at: '2026-03-01T16:01:00Z'
|
||||
},
|
||||
window: { guess_deadline_at: '2026-03-01T16:01:30Z' }
|
||||
} as T;
|
||||
}
|
||||
|
||||
throw { status: 404, error: { error: 'Not found' } };
|
||||
});
|
||||
|
||||
const client = createAngularApiClient({ get, post } as AngularHttpClientLike);
|
||||
|
||||
const showQuestion = await client.showQuestion('abcd12');
|
||||
expect(showQuestion.ok).toBe(true);
|
||||
|
||||
const mixAnswers = await client.mixAnswers('abcd12', 77);
|
||||
expect(mixAnswers.ok).toBe(true);
|
||||
|
||||
const calculateScores = await client.calculateScores('abcd12', 77);
|
||||
expect(calculateScores.ok).toBe(true);
|
||||
|
||||
const scoreboard = await client.getScoreboard('abcd12');
|
||||
expect(scoreboard.ok).toBe(true);
|
||||
|
||||
const nextRound = await client.startNextRound('abcd12');
|
||||
expect(nextRound.ok).toBe(true);
|
||||
|
||||
const finish = await client.finishGame('abcd12');
|
||||
expect(finish.ok).toBe(true);
|
||||
|
||||
const submitLie = await client.submitLie('abcd12', 77, { player_id: 9, session_token: 'tok', text: 'my lie' });
|
||||
expect(submitLie.ok).toBe(true);
|
||||
|
||||
const submitGuess = await client.submitGuess('abcd12', 77, {
|
||||
player_id: 9,
|
||||
session_token: 'tok',
|
||||
selected_text: 'A'
|
||||
});
|
||||
expect(submitGuess.ok).toBe(true);
|
||||
});
|
||||
|
||||
it('maps HttpErrorResponse-style failures to ApiResult errors', async () => {
|
||||
const http = {
|
||||
get: vi.fn<AngularHttpClientLike['get']>(async () => {
|
||||
|
||||
Reference in New Issue
Block a user