mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-04-27 07:48:01 -07:00
fix join game
This commit is contained in:
parent
2afa2922e9
commit
515dff6d7b
2 changed files with 70 additions and 5 deletions
|
|
@ -7,13 +7,21 @@ import {
|
|||
connectedWithRoomsState,
|
||||
} from '../../../__test-utils__';
|
||||
import { App, Data } from '@app/types';
|
||||
import { GameTypes } from '@app/store';
|
||||
import GameSelector from './GameSelector';
|
||||
|
||||
const { mockUseWebClient } = vi.hoisted(() => ({ mockUseWebClient: vi.fn() }));
|
||||
const { mockUseWebClient, mockNavigate } = vi.hoisted(() => ({
|
||||
mockUseWebClient: vi.fn(),
|
||||
mockNavigate: vi.fn(),
|
||||
}));
|
||||
vi.mock('@app/hooks', async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import('@app/hooks')>();
|
||||
return { ...actual, useWebClient: mockUseWebClient };
|
||||
});
|
||||
vi.mock('react-router-dom', async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import('react-router-dom')>();
|
||||
return { ...actual, useNavigate: () => mockNavigate };
|
||||
});
|
||||
|
||||
function makeRoomEntry(games: Data.ServerInfo_Game[] = [], gametypeMap: Record<number, string> = {}) {
|
||||
return {
|
||||
|
|
@ -81,6 +89,7 @@ function buildState(
|
|||
|
||||
beforeEach(() => {
|
||||
mockUseWebClient.mockReset();
|
||||
mockNavigate.mockReset();
|
||||
});
|
||||
|
||||
describe('GameSelector', () => {
|
||||
|
|
@ -209,6 +218,38 @@ describe('GameSelector', () => {
|
|||
expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('clicking Join on a game already present in games.games navigates to /game without sending a command', () => {
|
||||
const client = makeWebClient();
|
||||
mockUseWebClient.mockReturnValue(client);
|
||||
const game = makeGame({ gameId: 7, withPassword: false });
|
||||
const room = makeRoomEntry([game]);
|
||||
const state = buildState(room, makeUser(), 7);
|
||||
(state as any).games = { games: { 7: { info: { gameId: 7 } } } };
|
||||
renderWithProviders(<GameSelector room={room as any} />, { preloadedState: state });
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: /^Join$/ }));
|
||||
|
||||
expect(client.request.rooms.joinGame).not.toHaveBeenCalled();
|
||||
expect(mockNavigate).toHaveBeenCalledWith(App.RouteEnum.GAME);
|
||||
});
|
||||
|
||||
it('dispatching GAME_JOINED navigates to /game (mirrors JOIN_ROOM → /room)', async () => {
|
||||
mockUseWebClient.mockReturnValue(makeWebClient());
|
||||
const room = makeRoomEntry([]);
|
||||
const { store } = renderWithProviders(<GameSelector room={room as any} />, {
|
||||
preloadedState: buildState(room),
|
||||
});
|
||||
|
||||
mockNavigate.mockClear();
|
||||
store.dispatch({
|
||||
type: GameTypes.GAME_JOINED,
|
||||
payload: { data: { gameInfo: { gameId: 42 }, hostId: 0, playerId: 0, spectator: false } },
|
||||
});
|
||||
|
||||
await Promise.resolve();
|
||||
expect(mockNavigate).toHaveBeenCalledWith(App.RouteEnum.GAME);
|
||||
});
|
||||
|
||||
it('Join button is disabled while joinGamePending is true even when a game is selected', () => {
|
||||
mockUseWebClient.mockReturnValue(makeWebClient());
|
||||
const game = makeGame({ gameId: 7 });
|
||||
|
|
|
|||
|
|
@ -1,10 +1,18 @@
|
|||
import React, { useCallback, useState } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import Paper from '@mui/material/Paper';
|
||||
import Typography from '@mui/material/Typography';
|
||||
|
||||
import { RoomsDispatch, RoomsSelectors, ServerSelectors, useAppSelector } from '@app/store';
|
||||
import { useWebClient } from '@app/hooks';
|
||||
import type { App, Enriched } from '@app/types';
|
||||
import {
|
||||
GameSelectors,
|
||||
GameTypes,
|
||||
RoomsDispatch,
|
||||
RoomsSelectors,
|
||||
ServerSelectors,
|
||||
useAppSelector,
|
||||
} from '@app/store';
|
||||
import { useReduxEffect, useWebClient } from '@app/hooks';
|
||||
import { App, type Enriched } from '@app/types';
|
||||
import { AlertDialog, CreateGameDialog, FilterGamesDialog, PromptDialog } from '@app/dialogs';
|
||||
|
||||
import OpenGames from '../OpenGames';
|
||||
|
|
@ -25,6 +33,7 @@ interface PendingPasswordJoin {
|
|||
const GameSelector = ({ room }: GameSelectorProps) => {
|
||||
const roomId = room.info.roomId;
|
||||
const webClient = useWebClient();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const selectedGameId = useAppSelector((state) => RoomsSelectors.getSelectedGameId(state, roomId));
|
||||
const selectedGame = useAppSelector((state) =>
|
||||
|
|
@ -36,6 +45,13 @@ const GameSelector = ({ room }: GameSelectorProps) => {
|
|||
const isJudgeUser = useAppSelector(ServerSelectors.getIsUserJudge);
|
||||
const joinPending = useAppSelector(RoomsSelectors.getJoinGamePending);
|
||||
const joinError = useAppSelector(RoomsSelectors.getJoinGameError);
|
||||
const activeGameIds = useAppSelector(GameSelectors.getActiveGameIds);
|
||||
|
||||
// Mirrors Server.tsx's JOIN_ROOM → navigate(ROOM) pattern: when Event_GameJoined
|
||||
// lands, we're actually in the game — route to /game.
|
||||
useReduxEffect(() => {
|
||||
navigate(App.RouteEnum.GAME);
|
||||
}, GameTypes.GAME_JOINED, [navigate]);
|
||||
|
||||
const [createOpen, setCreateOpen] = useState(false);
|
||||
const [filterOpen, setFilterOpen] = useState(false);
|
||||
|
|
@ -43,6 +59,14 @@ const GameSelector = ({ room }: GameSelectorProps) => {
|
|||
|
||||
const sendJoin = useCallback(
|
||||
(gameId: number, asSpectator: boolean, asJudge: boolean, password: string) => {
|
||||
// Mirrors Rooms.tsx short-circuit: if we already have a live game entry
|
||||
// (Event_GameJoined has populated games.games[gameId]), skip the duplicate
|
||||
// JoinGame — the server would reject it with RespContextError — and go
|
||||
// straight to the game view.
|
||||
if (activeGameIds.includes(gameId)) {
|
||||
navigate(App.RouteEnum.GAME);
|
||||
return;
|
||||
}
|
||||
const params: App.JoinGameParams = {
|
||||
gameId,
|
||||
password,
|
||||
|
|
@ -52,7 +76,7 @@ const GameSelector = ({ room }: GameSelectorProps) => {
|
|||
};
|
||||
webClient.request.rooms.joinGame(roomId, params);
|
||||
},
|
||||
[roomId, webClient],
|
||||
[activeGameIds, navigate, roomId, webClient],
|
||||
);
|
||||
|
||||
const beginJoin = useCallback(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue