Add share pin
This commit is contained in:
parent
3e9a988748
commit
1078ece85c
5 changed files with 318 additions and 5 deletions
229
tests/components/Lobby.test.tsx
Normal file
229
tests/components/Lobby.test.tsx
Normal file
|
|
@ -0,0 +1,229 @@
|
|||
import React from 'react';
|
||||
import { render, screen, waitFor } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { Lobby } from '../../components/Lobby';
|
||||
|
||||
vi.mock('react-hot-toast', () => ({
|
||||
default: {
|
||||
success: vi.fn(),
|
||||
error: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('framer-motion', () => ({
|
||||
motion: {
|
||||
div: ({ children, ...props }: React.PropsWithChildren<Record<string, unknown>>) => <div {...props}>{children}</div>,
|
||||
},
|
||||
AnimatePresence: ({ children }: React.PropsWithChildren) => <>{children}</>,
|
||||
}));
|
||||
|
||||
describe('Lobby', () => {
|
||||
const defaultProps = {
|
||||
quizTitle: 'Test Quiz',
|
||||
players: [],
|
||||
gamePin: 'ABC123',
|
||||
role: 'HOST' as const,
|
||||
onStart: vi.fn(),
|
||||
onEndGame: vi.fn(),
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
Object.defineProperty(window, 'location', {
|
||||
value: { origin: 'http://localhost:5173' },
|
||||
writable: true,
|
||||
});
|
||||
});
|
||||
|
||||
describe('copy join link', () => {
|
||||
it('shows copy link button for host', () => {
|
||||
render(<Lobby {...defaultProps} />);
|
||||
|
||||
expect(screen.getByTitle('Copy join link')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('does not show copy link button for client', () => {
|
||||
render(<Lobby {...defaultProps} role="CLIENT" />);
|
||||
|
||||
expect(screen.queryByTitle('Copy join link')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('copies join URL to clipboard when clicked', async () => {
|
||||
const user = userEvent.setup();
|
||||
render(<Lobby {...defaultProps} />);
|
||||
|
||||
const copyButton = screen.getByTitle('Copy join link');
|
||||
expect(copyButton.querySelector('.lucide-link')).toBeInTheDocument();
|
||||
|
||||
await user.click(copyButton);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(copyButton.querySelector('.lucide-check')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it('shows success toast when link is copied', async () => {
|
||||
const toast = await import('react-hot-toast');
|
||||
const user = userEvent.setup();
|
||||
render(<Lobby {...defaultProps} />);
|
||||
|
||||
await user.click(screen.getByTitle('Copy join link'));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(toast.default.success).toHaveBeenCalledWith('Join link copied!');
|
||||
});
|
||||
});
|
||||
|
||||
it('shows checkmark after copying link', async () => {
|
||||
const user = userEvent.setup();
|
||||
render(<Lobby {...defaultProps} gamePin="XYZ789" />);
|
||||
|
||||
const copyButton = screen.getByTitle('Copy join link');
|
||||
|
||||
await user.click(copyButton);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(copyButton.querySelector('.lucide-check')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('display', () => {
|
||||
it('displays game PIN', () => {
|
||||
render(<Lobby {...defaultProps} />);
|
||||
|
||||
expect(screen.getByText('ABC123')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('displays quiz title on larger screens', () => {
|
||||
render(<Lobby {...defaultProps} />);
|
||||
|
||||
expect(screen.getByText('Test Quiz')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('displays player count', () => {
|
||||
const players = [
|
||||
{ id: '1', name: 'Player 1', score: 0, avatarSeed: 0.1 },
|
||||
{ id: '2', name: 'Player 2', score: 0, avatarSeed: 0.2 },
|
||||
];
|
||||
render(<Lobby {...defaultProps} players={players} />);
|
||||
|
||||
expect(screen.getByText('2')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows waiting message when no players have joined', () => {
|
||||
render(<Lobby {...defaultProps} />);
|
||||
|
||||
expect(screen.getByText('Waiting for players to join...')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('displays player names when players join', () => {
|
||||
const players = [
|
||||
{ id: '1', name: 'Alice', score: 0, avatarSeed: 0.1 },
|
||||
{ id: '2', name: 'Bob', score: 0, avatarSeed: 0.2 },
|
||||
];
|
||||
render(<Lobby {...defaultProps} players={players} />);
|
||||
|
||||
expect(screen.getByText('Alice')).toBeInTheDocument();
|
||||
expect(screen.getByText('Bob')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('host controls', () => {
|
||||
it('shows Start button for host', () => {
|
||||
render(<Lobby {...defaultProps} />);
|
||||
|
||||
expect(screen.getByRole('button', { name: 'Start' })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('disables Start button when no players', () => {
|
||||
render(<Lobby {...defaultProps} />);
|
||||
|
||||
expect(screen.getByRole('button', { name: 'Start' })).toBeDisabled();
|
||||
});
|
||||
|
||||
it('enables Start button when players joined', () => {
|
||||
const players = [{ id: '1', name: 'Player 1', score: 0, avatarSeed: 0.1 }];
|
||||
render(<Lobby {...defaultProps} players={players} />);
|
||||
|
||||
expect(screen.getByRole('button', { name: 'Start' })).not.toBeDisabled();
|
||||
});
|
||||
|
||||
it('calls onStart when Start clicked', async () => {
|
||||
const user = userEvent.setup();
|
||||
const players = [{ id: '1', name: 'Player 1', score: 0, avatarSeed: 0.1 }];
|
||||
render(<Lobby {...defaultProps} players={players} />);
|
||||
|
||||
await user.click(screen.getByRole('button', { name: 'Start' }));
|
||||
|
||||
expect(defaultProps.onStart).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('shows End Game button when onEndGame provided', () => {
|
||||
render(<Lobby {...defaultProps} />);
|
||||
|
||||
expect(screen.getByText('End Game')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('calls onEndGame when End Game clicked', async () => {
|
||||
const user = userEvent.setup();
|
||||
render(<Lobby {...defaultProps} />);
|
||||
|
||||
await user.click(screen.getByText('End Game'));
|
||||
|
||||
expect(defaultProps.onEndGame).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('client view', () => {
|
||||
it('shows waiting message for client', () => {
|
||||
render(<Lobby {...defaultProps} role="CLIENT" />);
|
||||
|
||||
expect(screen.getByText('Waiting for the host to start...')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows player name when currentPlayerId matches', () => {
|
||||
const players = [
|
||||
{ id: 'player-1', name: 'My Name', score: 0, avatarSeed: 0.5 },
|
||||
];
|
||||
render(
|
||||
<Lobby
|
||||
{...defaultProps}
|
||||
role="CLIENT"
|
||||
players={players}
|
||||
currentPlayerId="player-1"
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByText('My Name')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('does not show Start button for client', () => {
|
||||
render(<Lobby {...defaultProps} role="CLIENT" />);
|
||||
|
||||
expect(screen.queryByRole('button', { name: 'Start' })).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('host participates mode', () => {
|
||||
it('shows host in player list when hostParticipates is true', () => {
|
||||
const players = [
|
||||
{ id: 'host', name: 'Host Player', score: 0, avatarSeed: 0.99 },
|
||||
];
|
||||
render(<Lobby {...defaultProps} players={players} hostParticipates={true} />);
|
||||
|
||||
expect(screen.getByText('Host Player')).toBeInTheDocument();
|
||||
expect(screen.getByText('HOST')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('does not show waiting message when host participates and no other players', () => {
|
||||
const players = [
|
||||
{ id: 'host', name: 'Host Player', score: 0, avatarSeed: 0.99 },
|
||||
];
|
||||
render(<Lobby {...defaultProps} players={players} hostParticipates={true} />);
|
||||
|
||||
expect(screen.queryByText('Waiting for players to join...')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue