test(gameplay): stabilize canonical host gating specs
This commit is contained in:
@@ -4,6 +4,8 @@ import { HostShellComponent } from './host-shell.component';
|
||||
|
||||
type FetchMock = ReturnType<typeof vi.fn>;
|
||||
|
||||
type FetchRouteHandler = (input: RequestInfo | URL, init?: RequestInit) => Response | Promise<Response>;
|
||||
|
||||
function jsonResponse(status: number, body: unknown) {
|
||||
return {
|
||||
ok: status >= 200 && status < 300,
|
||||
@@ -12,6 +14,10 @@ function jsonResponse(status: number, body: unknown) {
|
||||
} as unknown as Response;
|
||||
}
|
||||
|
||||
function createFetchRouteMock(handler: FetchRouteHandler): FetchMock {
|
||||
return vi.fn((input: RequestInfo | URL, init?: RequestInit) => Promise.resolve(handler(input, init)));
|
||||
}
|
||||
|
||||
function sessionDetailPayload(
|
||||
status: string,
|
||||
options?: {
|
||||
@@ -180,14 +186,33 @@ describe('HostShellComponent gameplay wiring', () => {
|
||||
});
|
||||
|
||||
it('wires showQuestion, mixAnswers and calculateScores with canonical phase gating', async () => {
|
||||
const fetchMock: FetchMock = vi
|
||||
.fn()
|
||||
.mockResolvedValueOnce(jsonResponse(200, { session: { code: 'ABCD12', status: 'lie', current_round: 2 } }))
|
||||
.mockResolvedValueOnce(jsonResponse(200, sessionDetailPayload('lie', { roundQuestionId: 99 })))
|
||||
.mockResolvedValueOnce(jsonResponse(200, { session: { code: 'ABCD12', status: 'guess', current_round: 2 } }))
|
||||
.mockResolvedValueOnce(jsonResponse(200, sessionDetailPayload('guess', { roundQuestionId: 77 })))
|
||||
.mockResolvedValueOnce(jsonResponse(200, { session: { code: 'ABCD12', status: 'reveal', current_round: 2 } }))
|
||||
.mockResolvedValueOnce(jsonResponse(200, sessionDetailPayload('reveal', { roundQuestionId: 77 })));
|
||||
let refreshCount = 0;
|
||||
const fetchMock = createFetchRouteMock((input, init) => {
|
||||
const url = String(input);
|
||||
const method = init?.method ?? 'GET';
|
||||
|
||||
if (method === 'POST' && url === '/lobby/sessions/ABCD12/questions/show') {
|
||||
return jsonResponse(200, { session: { code: 'ABCD12', status: 'lie', current_round: 2 } });
|
||||
}
|
||||
if (method === 'POST' && url === '/lobby/sessions/ABCD12/questions/99/answers/mix') {
|
||||
return jsonResponse(200, { session: { code: 'ABCD12', status: 'guess', current_round: 2 } });
|
||||
}
|
||||
if (method === 'POST' && url === '/lobby/sessions/ABCD12/questions/77/scores/calculate') {
|
||||
return jsonResponse(200, { session: { code: 'ABCD12', status: 'reveal', current_round: 2 } });
|
||||
}
|
||||
if (method === 'GET' && url === '/lobby/sessions/ABCD12') {
|
||||
refreshCount += 1;
|
||||
if (refreshCount === 1) {
|
||||
return jsonResponse(200, sessionDetailPayload('lie', { roundQuestionId: 99 }));
|
||||
}
|
||||
if (refreshCount === 2) {
|
||||
return jsonResponse(200, sessionDetailPayload('guess', { roundQuestionId: 77 }));
|
||||
}
|
||||
return jsonResponse(200, sessionDetailPayload('reveal', { roundQuestionId: 77 }));
|
||||
}
|
||||
|
||||
throw new Error(`Unhandled fetch in test: ${method} ${url}`);
|
||||
});
|
||||
|
||||
vi.stubGlobal('fetch', fetchMock);
|
||||
|
||||
@@ -197,21 +222,35 @@ describe('HostShellComponent gameplay wiring', () => {
|
||||
|
||||
component.session = sessionDetailPayload('lie', { roundQuestionId: null }) as any;
|
||||
await component.showQuestion();
|
||||
expect(component.session?.session.status).toBe('lie');
|
||||
expect(component.roundQuestionId).toBe('99');
|
||||
|
||||
component.session = sessionDetailPayload('guess', { roundQuestionId: 77 }) as any;
|
||||
await component.mixAnswers();
|
||||
expect(component.session?.session.status).toBe('guess');
|
||||
|
||||
await component.calculateScores();
|
||||
|
||||
expect(component.session?.session.status).toBe('reveal');
|
||||
expect(component.error).toBe('');
|
||||
expect(component.loading).toBe(false);
|
||||
expect(fetchMock).toHaveBeenCalledTimes(6);
|
||||
});
|
||||
|
||||
it('runs next-round transition without reload and clears scoreboard payload', async () => {
|
||||
const fetchMock: FetchMock = vi
|
||||
.fn()
|
||||
.mockResolvedValueOnce(jsonResponse(200, { session: { code: 'ABCD12', status: 'lie', current_round: 2 } }))
|
||||
.mockResolvedValueOnce(jsonResponse(200, sessionDetailPayload('lie', { roundQuestionId: 99 })));
|
||||
const fetchMock = createFetchRouteMock((input, init) => {
|
||||
const url = String(input);
|
||||
const method = init?.method ?? 'GET';
|
||||
|
||||
if (method === 'POST' && url === '/lobby/sessions/ABCD12/rounds/next') {
|
||||
return jsonResponse(200, { session: { code: 'ABCD12', status: 'lie', current_round: 2 } });
|
||||
}
|
||||
if (method === 'GET' && url === '/lobby/sessions/ABCD12') {
|
||||
return jsonResponse(200, sessionDetailPayload('lie', { roundQuestionId: 99 }));
|
||||
}
|
||||
|
||||
throw new Error(`Unhandled fetch in test: ${method} ${url}`);
|
||||
});
|
||||
|
||||
vi.stubGlobal('fetch', fetchMock);
|
||||
|
||||
@@ -363,14 +402,17 @@ describe('HostShellComponent gameplay wiring', () => {
|
||||
|
||||
component.session = sessionDetailPayload('lie') as any;
|
||||
expect(component.canStartRound).toBe(false);
|
||||
expect(component.canShowQuestion).toBe(true);
|
||||
expect(component.canStartNextRound).toBe(false);
|
||||
expect(component.canFinishGame).toBe(false);
|
||||
|
||||
component.session = sessionDetailPayload('reveal') as any;
|
||||
expect(component.canLoadScoreboard).toBe(true);
|
||||
expect(component.canStartNextRound).toBe(true);
|
||||
expect(component.canFinishGame).toBe(true);
|
||||
|
||||
component.session = sessionDetailPayload('scoreboard') as any;
|
||||
expect(component.canLoadScoreboard).toBe(false);
|
||||
expect(component.canStartNextRound).toBe(false);
|
||||
expect(component.canFinishGame).toBe(false);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user