Skip to content

Commit

Permalink
Merge pull request #89 from hellomuthu23/add-custom-options
Browse files Browse the repository at this point in the history
feat: Add Custom card options feature
  • Loading branch information
hellomuthu23 committed Dec 14, 2023
2 parents 62ff1f8 + 2bbefb6 commit f53bcd8
Show file tree
Hide file tree
Showing 16 changed files with 496 additions and 92 deletions.
40 changes: 39 additions & 1 deletion src/components/Players/CardPicker/CardConfigs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,44 @@ export const getCards = (gameType: GameType | undefined): CardConfig[] => {
};

export const getRandomEmoji = () => {
const emojis = ['☕', '🥤', '🍹', '🍸', '🍧', '🍨', '🍩', '🍎', '🧁', '🍪', '🍿', '🌮', '🍦', '🍉', '🍐', '🍰', '🍫'];
const emojis = [
'☕',
'🥤',
'🍹',
'🍸',
'🍧',
'🍨',
'🍩',
'🍎',
'🧁',
'🍪',
'🍿',
'🌮',
'🍦',
'🍉',
'🍐',
'🍰',
'🍫',
];
return emojis[Math.floor(Math.random() * emojis.length)];
};

export const getCustomCards = (values: string[]) => {
const customCards: CardConfig[] = [
{ value: 0, displayValue: values[0], color: 'var(--color-background-secondary)' },
{ value: 1, displayValue: values[1], color: '#9EC8FE' },
{ value: 2, displayValue: values[2], color: '#9EC8FE' },
{ value: 3, displayValue: values[3], color: '#A3DFF2' },
{ value: 4, displayValue: values[4], color: '#A3DFF2' },
{ value: 5, displayValue: values[5], color: '#9DD49A' },
{ value: 6, displayValue: values[6], color: '#9DD49A' },
{ value: 7, displayValue: values[7], color: '#F4DD94' },
{ value: 8, displayValue: values[8], color: '#F4DD94' },
{ value: 9, displayValue: values[9], color: '#F39893' },
{ value: -2, displayValue: '❓', color: 'var(--color-background-secondary)' },
{ value: -1, displayValue: '-1', color: 'var(--color-background-secondary)' },
];
return customCards.filter(
(card) => card.displayValue !== undefined && card.displayValue.trim() !== '',
);
};
83 changes: 69 additions & 14 deletions src/components/Players/CardPicker/CardPicker.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import * as playersService from '../../../service/players';
import { Game, GameType } from '../../../types/game';
import { Player } from '../../../types/player';
import { Status } from '../../../types/status';
import { getCards } from './CardConfigs';
import { getCards, getCustomCards } from './CardConfigs';

Check warning on line 10 in src/components/Players/CardPicker/CardPicker.test.tsx

View workflow job for this annotation

GitHub Actions / build (18.x)

'getCustomCards' is defined but never used
import { CardPicker } from './CardPicker';
import * as cardConfigs from './CardConfigs';

Expand All @@ -18,6 +18,11 @@ describe('CardPicker component', () => {
name: 'testGame',
createdBy: 'someone',
createdAt: new Date(),
cards: [
{ value: 1, displayValue: '1', color: 'red' },
{ value: 2, displayValue: '2', color: 'blue' },
{ value: 3, displayValue: 'xl', color: 'green' },
],
gameType: GameType.Fibonacci,
average: 0,
createdById: 'abc',
Expand All @@ -29,7 +34,13 @@ describe('CardPicker component', () => {
];
const currentPlayerId = mockPlayers[0].id;
it('should display correct card values', () => {
const view = render(<CardPicker game={mockGame} players={mockPlayers} currentPlayerId={currentPlayerId} />);
const view = render(
<CardPicker
game={{ ...mockGame, cards: getCards(GameType.Fibonacci) }}
players={mockPlayers}
currentPlayerId={currentPlayerId}
/>,
);

getCards(GameType.Fibonacci)
.filter((a) => a.value >= 0)
Expand All @@ -43,10 +54,14 @@ describe('CardPicker component', () => {
it('should display correct card values for ShortFibonacci game type', () => {
const view = render(
<CardPicker
game={{ ...mockGame, gameType: GameType.ShortFibonacci }}
game={{
...mockGame,
cards: getCards(GameType.ShortFibonacci),
gameType: GameType.ShortFibonacci,
}}
players={mockPlayers}
currentPlayerId={currentPlayerId}
/>
/>,
);

getCards(GameType.ShortFibonacci)
Expand All @@ -61,10 +76,10 @@ describe('CardPicker component', () => {
it('should display correct card values TShirt game type', () => {
const view = render(
<CardPicker
game={{ ...mockGame, gameType: GameType.TShirt }}
game={{ ...mockGame, cards: getCards(GameType.TShirt), gameType: GameType.TShirt }}
players={mockPlayers}
currentPlayerId={currentPlayerId}
/>
/>,
);

getCards(GameType.TShirt)
Expand All @@ -79,10 +94,14 @@ describe('CardPicker component', () => {
it('should display correct card values TShirt & Numbers game type', () => {
const view = render(
<CardPicker
game={{ ...mockGame, gameType: GameType.TShirtAndNumber }}
game={{
...mockGame,
cards: getCards(GameType.TShirtAndNumber),
gameType: GameType.TShirtAndNumber,
}}
players={mockPlayers}
currentPlayerId={currentPlayerId}
/>
/>,
);

getCards(GameType.TShirtAndNumber)
Expand All @@ -94,15 +113,37 @@ describe('CardPicker component', () => {
expect(cardValueElement.length).toBeGreaterThan(0);
});
});
it('should display correct card values for Custom type', () => {
const view = render(
<CardPicker
game={{
...mockGame,

gameType: GameType.TShirtAndNumber,
}}
players={mockPlayers}
currentPlayerId={currentPlayerId}
/>,
);

mockGame.cards
.filter((a) => a.value >= 0)
.forEach((card) => {
const cardElement = view.container.querySelector(`#card-${card.displayValue}`);
expect(cardElement).toBeInTheDocument();
const cardValueElement = screen.queryAllByText(card.displayValue);
expect(cardValueElement.length).toBeGreaterThan(0);
});
});
it('should update player value when player clicks on a card', () => {
const currentPlayerId = mockPlayers[0].id;
const updatePlayerValueSpy = jest.spyOn(playersService, 'updatePlayerValue');
jest.spyOn(cardConfigs, 'getRandomEmoji').mockReturnValue('something');
render(<CardPicker game={mockGame} players={mockPlayers} currentPlayerId={currentPlayerId} />);
const cardValueElement = screen.queryAllByText(5);
const cardValueElement = screen.queryAllByText(1);
userEvent.click(cardValueElement[0]);
expect(updatePlayerValueSpy).toHaveBeenCalled();
expect(updatePlayerValueSpy).toHaveBeenCalledWith(mockGame.id, currentPlayerId, 5, 'something');
expect(updatePlayerValueSpy).toHaveBeenCalledWith(mockGame.id, currentPlayerId, 1, 'something');
});

it('should not update player value when player clicks on a card and game is finished', () => {
Expand All @@ -112,8 +153,14 @@ describe('CardPicker component', () => {
...mockGame,
gameStatus: Status.Finished,
};
render(<CardPicker game={finishedGameMock} players={mockPlayers} currentPlayerId={currentPlayerId} />);
const cardValueElement = screen.queryAllByText(5);
render(
<CardPicker
game={finishedGameMock}
players={mockPlayers}
currentPlayerId={currentPlayerId}
/>,
);
const cardValueElement = screen.queryAllByText(1);
userEvent.click(cardValueElement[0]);
expect(updatePlayerValueSpy).toHaveBeenCalledTimes(0);
});
Expand All @@ -131,8 +178,16 @@ describe('CardPicker component', () => {
...mockGame,
gameStatus: Status.Finished,
};
render(<CardPicker game={finishedGameMock} players={mockPlayers} currentPlayerId={currentPlayerId} />);
const helperText = screen.getByText('Session not ready for Voting! Wait for moderator to start');
render(
<CardPicker
game={finishedGameMock}
players={mockPlayers}
currentPlayerId={currentPlayerId}
/>,
);
const helperText = screen.getByText(
'Session not ready for Voting! Wait for moderator to start',
);

expect(helperText).toBeInTheDocument();
});
Expand Down
10 changes: 8 additions & 2 deletions src/components/Players/CardPicker/CardPicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,15 @@ export const CardPicker: React.FC<CardPickerProps> = ({ game, players, currentPl
updatePlayerValue(gameId, playerId, card.value, randomEmoji);
}
};
const cards = getCards(game.gameType);

useEffect(() => {
if (game.gameStatus === Status.Started) {
setRandomEmoji(getRandomEmoji);
}
}, [game.gameStatus]);

const cards = game.cards?.length ? game.cards : getCards(game.gameType);

return (
<Grow in={true} timeout={1000}>
<div>
Expand All @@ -51,7 +53,11 @@ export const CardPicker: React.FC<CardPickerProps> = ({ game, players, currentPl
<Typography className='CardContentTop' variant='caption'>
{card.displayValue}
</Typography>
<Typography className='CardContentMiddle' variant='h4'>

<Typography
className='CardContentMiddle'
variant={card.displayValue.length < 2 ? 'h4' : 'h5'}
>
{card.displayValue}
</Typography>
<Typography className='CardContentBottom' variant='caption'>
Expand Down
79 changes: 68 additions & 11 deletions src/components/Players/PlayerCard/PlayerCard.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ describe('PlayerCard component', () => {
const mockGame: Game = {
id: 'xyz',
name: 'testGame',
cards: [
{ value: 1, displayValue: '1', color: 'red' },
{ value: 2, displayValue: '2', color: 'blue' },
{ value: 3, displayValue: '3', color: 'green' },
],
createdBy: 'someone',
createdAt: new Date(),
average: 0,
Expand All @@ -19,71 +24,123 @@ describe('PlayerCard component', () => {
const mockPlayer: Player = { id: 'a1', name: 'SpiderMan', status: Status.InProgress, value: 0 };
let mockCurrentPlayerId = mockPlayer.id;
it('should display Player name', () => {
render(<PlayerCard game={mockGame} player={mockPlayer} currentPlayerId={mockCurrentPlayerId} />);
render(
<PlayerCard game={mockGame} player={mockPlayer} currentPlayerId={mockCurrentPlayerId} />,
);

expect(screen.getByText(mockPlayer.name)).toBeInTheDocument();
});

it('should display thinking emoji when Player has not voted', () => {
render(<PlayerCard game={mockGame} player={mockPlayer} currentPlayerId={mockCurrentPlayerId} />);
render(
<PlayerCard game={mockGame} player={mockPlayer} currentPlayerId={mockCurrentPlayerId} />,
);

expect(screen.getByText('🤔')).toBeInTheDocument();
});
it('should display thumbs up emoji when Player has voted', () => {
const votedPlayer = { ...mockPlayer, status: Status.Finished };
render(<PlayerCard game={mockGame} player={votedPlayer} currentPlayerId={mockCurrentPlayerId} />);
render(
<PlayerCard game={mockGame} player={votedPlayer} currentPlayerId={mockCurrentPlayerId} />,
);

expect(screen.getByText('👍')).toBeInTheDocument();
});

it('should display coffee up emoji when Player has voted but value is -1 and Game is finished', () => {
const coffeePlayer = { ...mockPlayer, status: Status.Finished, value: -1 };
const finishedGame = { ...mockGame, gameStatus: Status.Finished };
render(<PlayerCard game={finishedGame} player={coffeePlayer} currentPlayerId={mockCurrentPlayerId} />);
render(
<PlayerCard
game={finishedGame}
player={coffeePlayer}
currentPlayerId={mockCurrentPlayerId}
/>,
);

expect(screen.getByText('☕')).toBeInTheDocument();
});

it('should display correct when Player has voted and Game is finished', () => {
const coffeePlayer = { ...mockPlayer, status: Status.Finished, value: 5 };
const finishedGame = { ...mockGame, gameStatus: Status.Finished };
render(<PlayerCard game={finishedGame} player={coffeePlayer} currentPlayerId={mockCurrentPlayerId} />);
render(
<PlayerCard
game={finishedGame}
player={coffeePlayer}
currentPlayerId={mockCurrentPlayerId}
/>,
);

expect(screen.getByText('5')).toBeInTheDocument();
});
it('should display thinking emoji when Player has not voted and Game is finished', () => {
const coffeePlayer = { ...mockPlayer, status: Status.InProgress };
const finishedGame = { ...mockGame, gameStatus: Status.Finished };
render(<PlayerCard game={finishedGame} player={coffeePlayer} currentPlayerId={mockCurrentPlayerId} />);
render(
<PlayerCard
game={finishedGame}
player={coffeePlayer}
currentPlayerId={mockCurrentPlayerId}
/>,
);

expect(screen.getByText('🤔')).toBeInTheDocument();
});
it('should display remove icon for moderator', () => {
const coffeePlayer = { ...mockPlayer, status: Status.InProgress };
const finishedGame = { ...mockGame, gameStatus: Status.Finished };
render(<PlayerCard game={finishedGame} player={coffeePlayer} currentPlayerId={mockGame.createdById} />);
render(
<PlayerCard
game={finishedGame}
player={coffeePlayer}
currentPlayerId={mockGame.createdById}
/>,
);

expect(screen.getByTestId('remove-button')).toBeInTheDocument();
});
it('should not display remove icon for non moderator', () => {
const coffeePlayer = { ...mockPlayer, status: Status.InProgress };
const finishedGame = { ...mockGame, gameStatus: Status.Finished };
render(<PlayerCard game={finishedGame} player={coffeePlayer} currentPlayerId={mockCurrentPlayerId} />);
render(
<PlayerCard
game={finishedGame}
player={coffeePlayer}
currentPlayerId={mockCurrentPlayerId}
/>,
);

expect(screen.queryByTestId('remove-button')).not.toBeInTheDocument();
});
it('should not display remove icon for moderator card', () => {
const coffeePlayer = { ...mockPlayer, status: Status.InProgress };
const finishedGame = { ...mockGame, createdBy: mockCurrentPlayerId, gameStatus: Status.Finished };
render(<PlayerCard game={finishedGame} player={coffeePlayer} currentPlayerId={mockCurrentPlayerId} />);
const finishedGame = {
...mockGame,
createdBy: mockCurrentPlayerId,
gameStatus: Status.Finished,
};
render(
<PlayerCard
game={finishedGame}
player={coffeePlayer}
currentPlayerId={mockCurrentPlayerId}
/>,
);

expect(screen.queryByTestId('remove-button')).not.toBeInTheDocument();
});
it('should call remove function on Remove action', () => {
const coffeePlayer = { ...mockPlayer, status: Status.InProgress };
const finishedGame = { ...mockGame, gameStatus: Status.Finished };
jest.spyOn(playerService, 'removePlayer').mockResolvedValue();
render(<PlayerCard game={finishedGame} player={coffeePlayer} currentPlayerId={mockGame.createdById} />);
render(
<PlayerCard
game={finishedGame}
player={coffeePlayer}
currentPlayerId={mockGame.createdById}
/>,
);

userEvent.click(screen.getByTestId('remove-button'));
expect(playerService.removePlayer).toHaveBeenCalledWith(finishedGame.id, coffeePlayer.id);
Expand Down
Loading

0 comments on commit f53bcd8

Please sign in to comment.