Skip to content

Commit

Permalink
refactor(web-ui): add useGame
Browse files Browse the repository at this point in the history
  • Loading branch information
notaphplover committed Sep 23, 2024
1 parent 9ffe4f5 commit 6ae6071
Show file tree
Hide file tree
Showing 2 changed files with 338 additions and 0 deletions.
271 changes: 271 additions & 0 deletions packages/frontend/web-ui/src/game/hooks/useGame.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,271 @@
import { afterAll, beforeAll, describe, expect, it, jest } from '@jest/globals';

jest.mock('../../common/hooks/useRedirectUnauthorized');
jest.mock('../../common/hooks/useUrlLikeLocation');
jest.mock('../../user/hooks/useGetUserMe');
jest.mock('../helpers/getGameSlotIndex');
jest.mock('./useGameCards');
jest.mock('./useGetGamesV1GameId');
jest.mock('./useGetGamesV1GameIdSlotsSlotIdCards');

import { models as apiModels } from '@cornie-js/api-models';
import { renderHook, RenderHookResult } from '@testing-library/react';

import { useRedirectUnauthorized } from '../../common/hooks/useRedirectUnauthorized';
import { useUrlLikeLocation } from '../../common/hooks/useUrlLikeLocation';
import { UrlLikeLocation } from '../../common/models/UrlLikeLocation';
import { useGetUserMe } from '../../user/hooks/useGetUserMe';
import { getGameSlotIndex } from '../helpers/getGameSlotIndex';
import { useGame, UseGameResult } from './useGame';
import { useGameCards, UseGameCardsResult } from './useGameCards';
import { useGetGamesV1GameId } from './useGetGamesV1GameId';
import { useGetGamesV1GameIdSlotsSlotIdCards } from './useGetGamesV1GameIdSlotsSlotIdCards';

describe(useGame.name, () => {
describe('when called, and queries return null result', () => {
let gameIdFixture: string;
let urlLikeLocationFixture: UrlLikeLocation;
let useGameCardsResultFixture: UseGameCardsResult;

let renderResult: RenderHookResult<UseGameResult, unknown>;

beforeAll(() => {
gameIdFixture = 'game-id-fixture';

urlLikeLocationFixture = {
pathname: '/path',
searchParams: new URLSearchParams(`?gameId=${gameIdFixture}`),
} as Partial<UrlLikeLocation> as UrlLikeLocation;

useGameCardsResultFixture = {
cards: [],
hasNext: false,
hasPrevious: false,
setNext: jest.fn(),
setPrevious: jest.fn(),
};

(
useUrlLikeLocation as jest.Mock<typeof useUrlLikeLocation>
).mockReturnValueOnce(urlLikeLocationFixture);

(useGetUserMe as jest.Mock<typeof useGetUserMe>).mockReturnValueOnce({
result: null,
});

(
useGetGamesV1GameId as jest.Mock<typeof useGetGamesV1GameId>
).mockReturnValueOnce({ result: null });

(
useGetGamesV1GameIdSlotsSlotIdCards as jest.Mock<
typeof useGetGamesV1GameIdSlotsSlotIdCards
>
).mockReturnValueOnce({ result: null });

(useGameCards as jest.Mock<typeof useGameCards>).mockReturnValueOnce(
useGameCardsResultFixture,
);

renderResult = renderHook(() => useGame());
});

afterAll(() => {
jest.clearAllMocks();
});

it('should call useRedirectUnauthorized()', () => {
expect(useRedirectUnauthorized).toHaveBeenCalledTimes(1);
expect(useRedirectUnauthorized).toHaveBeenCalledWith();
});

it('should call useGetUserMe()', () => {
expect(useGetUserMe).toHaveBeenCalledTimes(1);
expect(useGetUserMe).toHaveBeenCalledWith();
});

it('should call useGetGamesV1GameId()', () => {
expect(useGetGamesV1GameId).toHaveBeenCalledTimes(1);
expect(useGetGamesV1GameId).toHaveBeenCalledWith(gameIdFixture);
});

it('should not call getGameSlotIndex()', () => {
expect(getGameSlotIndex).not.toHaveBeenCalled();
});

it('should call useGetGamesV1GameIdSlotsSlotIdCards()', () => {
expect(useGetGamesV1GameIdSlotsSlotIdCards).toHaveBeenCalledTimes(1);
expect(useGetGamesV1GameIdSlotsSlotIdCards).toHaveBeenCalledWith(
gameIdFixture,
null,
);
});

it('should call useGameCards()', () => {
expect(useGameCards).toHaveBeenCalledTimes(1);
expect(useGameCards).toHaveBeenCalledWith([]);
});

it('should retuen expected result', () => {
const expected: UseGameResult = {
currentCard: undefined,
game: undefined,
isPending: true,
useGameCardsResult: useGameCardsResultFixture,
};

expect(renderResult.result.current).toStrictEqual(expected);
});
});

describe('when called, and queries return non null results', () => {
let gameCardsFixture: apiModels.CardArrayV1;
let gameFixture: apiModels.ActiveGameV1;
let gameIdFixture: string;
let gameSlotIndexFixture: string;
let urlLikeLocationFixture: UrlLikeLocation;
let userFixture: apiModels.UserV1;
let useGameCardsResultFixture: UseGameCardsResult;

let renderResult: RenderHookResult<UseGameResult, unknown>;

beforeAll(() => {
gameCardsFixture = [
{
kind: 'wildDraw4',
},
];

gameFixture = {
id: 'game-id-fixture',
isPublic: true,
state: {
currentCard: {
kind: 'wild',
},
currentColor: 'blue',
currentDirection: 'clockwise',
currentPlayingSlotIndex: 0,
currentTurnCardsDrawn: false,
currentTurnCardsPlayed: false,
drawCount: 0,
lastEventId: 'last-event-id-fixture',
slots: [],
status: 'active',
},
};

gameSlotIndexFixture = '0';

userFixture = {
active: true,
id: 'user-id-fixture',
name: 'user-name-fixture',
};

gameIdFixture = gameFixture.id;

urlLikeLocationFixture = {
pathname: '/path',
searchParams: new URLSearchParams(`?gameId=${gameIdFixture}`),
} as Partial<UrlLikeLocation> as UrlLikeLocation;

useGameCardsResultFixture = {
cards: [],
hasNext: false,
hasPrevious: false,
setNext: jest.fn(),
setPrevious: jest.fn(),
};

(
useUrlLikeLocation as jest.Mock<typeof useUrlLikeLocation>
).mockReturnValueOnce(urlLikeLocationFixture);

(useGetUserMe as jest.Mock<typeof useGetUserMe>).mockReturnValueOnce({
result: {
isRight: true,
value: userFixture,
},
});

(
useGetGamesV1GameId as jest.Mock<typeof useGetGamesV1GameId>
).mockReturnValueOnce({
result: {
isRight: true,
value: gameFixture,
},
});

(
useGetGamesV1GameIdSlotsSlotIdCards as jest.Mock<
typeof useGetGamesV1GameIdSlotsSlotIdCards
>
).mockReturnValueOnce({
result: {
isRight: true,
value: gameCardsFixture,
},
});

(
getGameSlotIndex as jest.Mock<typeof getGameSlotIndex>
).mockReturnValueOnce(gameSlotIndexFixture);

(useGameCards as jest.Mock<typeof useGameCards>).mockReturnValueOnce(
useGameCardsResultFixture,
);

renderResult = renderHook(() => useGame());
});

afterAll(() => {
jest.clearAllMocks();
});

it('should call useRedirectUnauthorized()', () => {
expect(useRedirectUnauthorized).toHaveBeenCalledTimes(1);
expect(useRedirectUnauthorized).toHaveBeenCalledWith();
});

it('should call useGetUserMe()', () => {
expect(useGetUserMe).toHaveBeenCalledTimes(1);
expect(useGetUserMe).toHaveBeenCalledWith();
});

it('should call useGetGamesV1GameId()', () => {
expect(useGetGamesV1GameId).toHaveBeenCalledTimes(1);
expect(useGetGamesV1GameId).toHaveBeenCalledWith(gameIdFixture);
});

it('should call getGameSlotIndex()', () => {
expect(getGameSlotIndex).toHaveBeenCalledTimes(1);
expect(getGameSlotIndex).toHaveBeenCalledWith(gameFixture, userFixture);
});

it('should call useGetGamesV1GameIdSlotsSlotIdCards()', () => {
expect(useGetGamesV1GameIdSlotsSlotIdCards).toHaveBeenCalledTimes(1);
expect(useGetGamesV1GameIdSlotsSlotIdCards).toHaveBeenCalledWith(
gameIdFixture,
gameSlotIndexFixture,
);
});

it('should call useGameCards()', () => {
expect(useGameCards).toHaveBeenCalledTimes(1);
expect(useGameCards).toHaveBeenCalledWith(gameCardsFixture);
});

it('should retuen expected result', () => {
const expected: UseGameResult = {
currentCard: gameFixture.state.currentCard,
game: gameFixture,
isPending: false,
useGameCardsResult: useGameCardsResultFixture,
};

expect(renderResult.result.current).toStrictEqual(expected);
});
});
});
67 changes: 67 additions & 0 deletions packages/frontend/web-ui/src/game/hooks/useGame.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { models as apiModels } from '@cornie-js/api-models';

import { useRedirectUnauthorized } from '../../common/hooks/useRedirectUnauthorized';
import { useUrlLikeLocation } from '../../common/hooks/useUrlLikeLocation';
import { UrlLikeLocation } from '../../common/models/UrlLikeLocation';
import { useGetUserMe } from '../../user/hooks/useGetUserMe';
import { getGameSlotIndex } from '../helpers/getGameSlotIndex';
import { useGameCards, UseGameCardsResult } from './useGameCards';
import { useGetGamesV1GameId } from './useGetGamesV1GameId';
import { useGetGamesV1GameIdSlotsSlotIdCards } from './useGetGamesV1GameIdSlotsSlotIdCards';

export interface UseGameResult {
currentCard: apiModels.CardV1 | undefined;
game: apiModels.GameV1 | undefined;
isPending: boolean;
useGameCardsResult: UseGameCardsResult;
}

function getGameCurrentCard(
game: apiModels.GameV1 | undefined,
): apiModels.CardV1 | undefined {
return game?.state.status === 'active' ? game.state.currentCard : undefined;
}

export const useGame = (): UseGameResult => {
useRedirectUnauthorized();

const url: UrlLikeLocation = useUrlLikeLocation();
const gameIdParam: string | null = url.searchParams.get('gameId');

const { result: usersV1MeResult } = useGetUserMe();

const { result: gamesV1GameIdResult } = useGetGamesV1GameId(gameIdParam);

const game: apiModels.GameV1 | undefined =
gamesV1GameIdResult?.isRight === true
? gamesV1GameIdResult.value
: undefined;

const currentCard: apiModels.CardV1 | undefined = getGameCurrentCard(game);

const gameSlotIndexParam: string | null =
usersV1MeResult?.isRight === true
? getGameSlotIndex(game, usersV1MeResult.value)
: null;

const { result: gamesV1GameIdSlotsSlotIdCardsResult } =
useGetGamesV1GameIdSlotsSlotIdCards(gameIdParam, gameSlotIndexParam);

const isPending =
gamesV1GameIdResult === null ||
gamesV1GameIdSlotsSlotIdCardsResult === null;

const gameCards: apiModels.CardArrayV1 =
gamesV1GameIdSlotsSlotIdCardsResult?.isRight === true
? gamesV1GameIdSlotsSlotIdCardsResult.value
: [];

const useGameCardsResult: UseGameCardsResult = useGameCards(gameCards);

return {
currentCard,
game,
isPending,
useGameCardsResult,
};
};

0 comments on commit 6ae6071

Please sign in to comment.