Skip to content

Commit

Permalink
Merge pull request #1604 from notaphplover/feat/add-public-games-page
Browse files Browse the repository at this point in the history
Add public games page
  • Loading branch information
notaphplover authored Aug 31, 2024
2 parents 5621039 + 6001224 commit 1e6cc5d
Show file tree
Hide file tree
Showing 16 changed files with 531 additions and 82 deletions.
15 changes: 15 additions & 0 deletions packages/frontend/web-ui/src/common/components/NavbarGamesMenu.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import AddIcon from '@mui/icons-material/Add';
import PublicIcon from '@mui/icons-material/Public';
import { Box, Button, Menu, MenuItem } from '@mui/material';

import { getSlug } from '../helpers/getSlug';
Expand Down Expand Up @@ -34,6 +35,20 @@ const NavbarGamesMenuItems = (
NEW GAME
</Button>
</MenuItem>
<MenuItem
className="navbar-menu-item"
key={PageName.publicGames}
onClick={buildCloseMenu(params)}
>
<Button
component="a"
className="navbar-link"
href={getSlug(PageName.publicGames)}
startIcon={<PublicIcon />}
>
PUBLIC GAMES
</Button>
</MenuItem>
</Box>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export const cornieApi: jest.Mocked<typeof originalCornieApi> = {
useCreateUsersV1EmailCodeMutation: jest.fn(),
useCreateUsersV1Mutation: jest.fn(),
useGetGamesV1MineQuery: jest.fn(),
useGetGamesV1Query: jest.fn(),
useGetUsersV1MeDetailQuery: jest.fn(),
useGetUsersV1MeQuery: jest.fn(),
useUpdateUsersV1MeMutation: jest.fn(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ describe(NonStartedGameList.name, () => {
beforeAll(() => {
optionsFixture = {
gamesResult: null,
usersMeResult: null,
};
});

Expand All @@ -40,7 +41,7 @@ describe(NonStartedGameList.name, () => {
);

const renderResult: RenderResult = render(
<NonStartedGameList gamesResult={optionsFixture.gamesResult} />,
<NonStartedGameList {...optionsFixture} />,
);

const baseGameList: ChildNode | undefined =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,11 @@ export interface NonStartedGameListButtonsOptions {
}

export interface NonStartedGameListOptions {
buttons?: NonStartedGameListButtonsOptions;
buttons?: NonStartedGameListButtonsOptions | undefined;
pagination?: BaseGameListPaginationOptions | undefined;
title?: string | undefined;
gamesResult: Either<string, apiModels.GameArrayV1> | null;
usersMeResult: Either<string, apiModels.UserV1> | null;
}

function buildGameItemBuilder(
Expand Down Expand Up @@ -69,7 +70,8 @@ export const NonStartedGameList = (options: NonStartedGameListOptions) => {
></Snackbar>
<BaseGameList
buildGameItem={buildGameItemBuilder(enqueue, options)}
{...options}
gamesResult={options.gamesResult}
pagination={options.pagination}
/>
</>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import { afterAll, beforeAll, describe, expect, it, jest } from '@jest/globals';

jest.mock('../../common/helpers/getSlug');
jest.mock('../helpers/userCanJoinGame');
jest.mock('./BaseGameListItem');

import { render, RenderResult } from '@testing-library/react';
import React from 'react';

import { getSlug } from '../../common/helpers/getSlug';
import { PageName } from '../../common/models/PageName';
import { userCanJoinGame } from '../helpers/userCanJoinGame';
import { BaseGameListItem, BaseGameListItemOptions } from './BaseGameListItem';
import {
NonStartedGameListItem,
Expand Down Expand Up @@ -38,12 +40,14 @@ describe(NonStartedGameListItem.name, () => {
describe('when called', () => {
let baseGameListItemContentFixture: string;
let slugFixture: string;
let userCanJoinGameResultFixture: boolean;

let baseGameListItemContent: string | null | undefined;

beforeAll(() => {
baseGameListItemContentFixture = 'Expected content fixture';
slugFixture = '/slug-fixture';
userCanJoinGameResultFixture = true;

const baseGameListItemFixture = (
<div className="base-game-list-item-fixture">
Expand All @@ -57,6 +61,10 @@ describe(NonStartedGameListItem.name, () => {

(getSlug as jest.Mock<typeof getSlug>).mockReturnValueOnce(slugFixture);

(
userCanJoinGame as jest.Mock<typeof userCanJoinGame>
).mockReturnValueOnce(userCanJoinGameResultFixture);

const renderResult: RenderResult = render(
<NonStartedGameListItem {...nonStartedGameListItemOptionsMock} />,
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { MouseEvent, MouseEventHandler } from 'react';

import { getSlug } from '../../common/helpers/getSlug';
import { PageName } from '../../common/models/PageName';
import { userCanJoinGame } from '../helpers/userCanJoinGame';
import { BaseGameListItem } from './BaseGameListItem';

export type NonStartedGameListItemButtonOptions =
Expand All @@ -20,6 +21,7 @@ export interface NonStartedGameListItemButtonsOptions {

export interface NonStartedGameListItemOptions {
buttons?: NonStartedGameListItemButtonsOptions;
user?: apiModels.UserV1;
game: apiModels.GameV1;
}

Expand Down Expand Up @@ -63,6 +65,7 @@ export const NonStartedGameListItem = (
<Button
className="game-list-item-button"
component="a"
disabled={!userCanJoinGame(options.game, options.user)}
href={`${getSlug(PageName.joinGame)}?gameId=${options.game.id}`}
startIcon={<JoinInner />}
>
Expand Down
140 changes: 140 additions & 0 deletions packages/frontend/web-ui/src/game/helpers/userCanJoinGame.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import { beforeAll, describe, expect, it } from '@jest/globals';

import { models as apiModels } from '@cornie-js/api-models';

import { userCanJoinGame } from './userCanJoinGame';

describe(userCanJoinGame.name, () => {
describe('having an undefined user', () => {
let gameV1Fixture: apiModels.GameV1;

beforeAll(() => {
gameV1Fixture = {
id: 'game-id',
isPublic: false,
state: {
slots: [],
status: 'nonStarted',
},
};
});

describe('when called', () => {
let result: unknown;

beforeAll(() => {
result = userCanJoinGame(gameV1Fixture, undefined);
});

it('should return false', () => {
expect(result).toBe(false);
});
});
});

describe('having an user and a finished game', () => {
let gameV1Fixture: apiModels.FinishedGameV1;
let userV1Fixture: apiModels.UserV1;

beforeAll(() => {
gameV1Fixture = {
id: 'game-id',
isPublic: false,
state: {
slots: [],
status: 'finished',
},
};

userV1Fixture = {
active: true,
id: 'id',
name: 'name',
};
});

describe('when called', () => {
let result: unknown;

beforeAll(() => {
result = userCanJoinGame(gameV1Fixture, userV1Fixture);
});

it('should return false', () => {
expect(result).toBe(false);
});
});
});

describe('having an user and a non started game with slot with user id', () => {
let gameV1Fixture: apiModels.NonStartedGameV1;
let userV1Fixture: apiModels.UserV1;

beforeAll(() => {
userV1Fixture = {
active: true,
id: 'id',
name: 'name',
};

gameV1Fixture = {
id: 'game-id',
isPublic: false,
state: {
slots: [
{
userId: userV1Fixture.id,
},
],
status: 'nonStarted',
},
};
});

describe('when called', () => {
let result: unknown;

beforeAll(() => {
result = userCanJoinGame(gameV1Fixture, userV1Fixture);
});

it('should return false', () => {
expect(result).toBe(false);
});
});
});

describe('having an user and a non started game without slot with user id', () => {
let gameV1Fixture: apiModels.NonStartedGameV1;
let userV1Fixture: apiModels.UserV1;

beforeAll(() => {
userV1Fixture = {
active: true,
id: 'id',
name: 'name',
};

gameV1Fixture = {
id: 'game-id',
isPublic: false,
state: {
slots: [],
status: 'nonStarted',
},
};
});

describe('when called', () => {
let result: unknown;

beforeAll(() => {
result = userCanJoinGame(gameV1Fixture, userV1Fixture);
});

it('should return true', () => {
expect(result).toBe(true);
});
});
});
});
15 changes: 15 additions & 0 deletions packages/frontend/web-ui/src/game/helpers/userCanJoinGame.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { models as apiModels } from '@cornie-js/api-models';

export function userCanJoinGame(
game: apiModels.GameV1,
user: apiModels.UserV1 | undefined,
): boolean {
return (
user !== undefined &&
game.state.status === 'nonStarted' &&
!game.state.slots.some(
(slot: apiModels.NonStartedGameSlotV1): boolean =>
slot.userId === user.id,
)
);
}
Loading

0 comments on commit 1e6cc5d

Please sign in to comment.