import React from 'react'; import { render, screen, fireEvent, waitFor } from '@testing-library/react'; import '@testing-library/jest-dom'; import axios from 'axios'; import App from '../App'; // Mock axios jest.mock('axios'); const mockedAxios = axios; // Mock localStorage const localStorageMock = { getItem: jest.fn(), setItem: jest.fn(), removeItem: jest.fn(), clear: jest.fn(), }; global.localStorage = localStorageMock; describe('Login Functionality', () => { beforeEach(() => { jest.clearAllMocks(); localStorageMock.getItem.mockReturnValue(null); // Mock axios to handle different endpoints mockedAxios.get.mockImplementation((url) => { if (url === '/api/players/names') { return Promise.resolve({ data: [ { name: 'gm' }, { name: 'anders' }, { name: 'phillip' } ] }); } if (url === '/api/missions') { return Promise.resolve({ data: [] }); } if (url === '/api/missions/active/current') { return Promise.resolve({ data: null }); } if (url === '/api/missions/active/player') { return Promise.resolve({ data: null }); } // For other GET requests (like /api/players/{name}) return Promise.resolve({ data: { name: 'gm', tabInfo: { rp: 100, xp: 1000, xpSpent: 200, renown: 'Respected', charName: 'Game Master' } } }); }); // Default POST mock - will be overridden per test with mockResolvedValueOnce mockedAxios.post.mockImplementation((url, data) => { if (url === '/api/players/login') { return Promise.resolve({ data: { success: true, sessionId: 'default_session_id', player: { name: data.name } }, status: 200 }); } if (url === '/api/sessions/validate') { const sessionId = data?.sessionId || ''; const playerName = sessionId.includes('anders') ? 'anders' : sessionId.includes('gm') ? 'gm' : ''; return Promise.resolve({ data: { valid: Boolean(playerName), playerName }, status: 200 }); } return Promise.resolve({ status: 200 }); }); }); test('login with GM account', async () => { mockedAxios.post.mockResolvedValueOnce({ data: { success: true, sessionId: 'session_gm_12345', player: { name: 'gm' }, expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString() }, status: 200 }); render(); // Wait for the login form to appear await waitFor(() => { expect(screen.getByPlaceholderText(/username/i)).toBeInTheDocument(); }); const nameInput = screen.getByPlaceholderText(/username/i); const passwordInput = screen.getByPlaceholderText(/password/i); const loginButton = screen.getByRole('button', { name: /login/i }); // Fill in login form with GM credentials fireEvent.change(nameInput, { target: { value: 'gm' } }); fireEvent.change(passwordInput, { target: { value: 'bongo' } }); // Click login button fireEvent.click(loginButton); // Verify login was called with correct parameters await waitFor(() => { expect(mockedAxios.post).toHaveBeenCalledWith( '/api/players/login', { name: 'gm', password: 'bongo' } ); }); // Verify success message appears await waitFor(() => { expect(screen.getByText(/login successful/i)).toBeInTheDocument(); }, { timeout: 3000 }); }); test('login with invalid password fails', async () => { mockedAxios.post.mockImplementation((url) => { if (url === '/api/players/login') { return Promise.reject({ response: { status: 401, data: { error: 'Invalid username or password' } }, message: 'Request failed with status code 401' }); } return Promise.resolve({ data: {}, status: 200 }); }); render(); await waitFor(() => { expect(screen.getByPlaceholderText(/username/i)).toBeInTheDocument(); }); const nameInput = screen.getByPlaceholderText(/username/i); const passwordInput = screen.getByPlaceholderText(/password/i); const loginButton = screen.getByRole('button', { name: /login/i }); fireEvent.change(nameInput, { target: { value: 'gm' } }); fireEvent.change(passwordInput, { target: { value: 'wrong_password' } }); fireEvent.click(loginButton); await waitFor(() => { expect(screen.getByText(/login failed/i)).toBeInTheDocument(); }); }); test('login with non-existent player fails', async () => { mockedAxios.post.mockImplementation((url) => { if (url === '/api/players/login') { return Promise.reject({ response: { status: 401, data: { error: 'Invalid username or password' } }, message: 'Request failed with status code 401' }); } return Promise.resolve({ data: {}, status: 200 }); }); render(); await waitFor(() => { expect(screen.getByPlaceholderText(/username/i)).toBeInTheDocument(); }); const nameInput = screen.getByPlaceholderText(/username/i); const passwordInput = screen.getByPlaceholderText(/password/i); const loginButton = screen.getByRole('button', { name: /login/i }); fireEvent.change(nameInput, { target: { value: 'nonexistent' } }); fireEvent.change(passwordInput, { target: { value: '1234' } }); fireEvent.click(loginButton); await waitFor(() => { expect(screen.getByText(/login failed/i)).toBeInTheDocument(); }); }); test('login with player account', async () => { mockedAxios.post.mockResolvedValueOnce({ data: { success: true, sessionId: 'session_anders_12345', player: { name: 'anders' }, expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString() }, status: 200 }); render(); // Wait for the login form to appear await waitFor(() => { expect(screen.getByPlaceholderText(/username/i)).toBeInTheDocument(); }); // Find login inputs const nameInput = screen.getByPlaceholderText(/username/i); const passwordInput = screen.getByPlaceholderText(/password/i); const loginButton = screen.getByRole('button', { name: /login/i }); // Fill in login form with player credentials fireEvent.change(nameInput, { target: { value: 'anders' } }); fireEvent.change(passwordInput, { target: { value: '1234' } }); // Click login button fireEvent.click(loginButton); // Verify login was called with correct parameters await waitFor(() => { expect(mockedAxios.post).toHaveBeenCalledWith( '/api/players/login', { name: 'anders', password: '1234' } ); }); // Verify success message appears await waitFor(() => { expect(screen.getByText(/login successful/i)).toBeInTheDocument(); }, { timeout: 3000 }); await waitFor(() => { expect(screen.getAllByText(/active mission/i).length).toBeGreaterThan(0); }); expect(screen.queryByText(/mission is only accessible to game masters/i)).not.toBeInTheDocument(); }); });