diff --git a/docs/src/add20Users.png b/docs/images/add20Users.png
similarity index 100%
rename from docs/src/add20Users.png
rename to docs/images/add20Users.png
diff --git a/docs/src/playGame100.png b/docs/images/playGame100.png
similarity index 100%
rename from docs/src/playGame100.png
rename to docs/images/playGame100.png
diff --git a/package-lock.json b/package-lock.json
index 29b2e41a..9319c1ed 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,5 +1,5 @@
{
- "name": "WIQ_ES2B",
+ "name": "wiq_es2b",
"lockfileVersion": 2,
"requires": true,
"packages": {
@@ -12,6 +12,7 @@
"axios": "^1.6.8",
"cors": "^2.8.5",
"express": "^4.19.2",
+ "history": "^5.3.0",
"jest": "^29.7.0",
"supertest": "^6.3.4"
},
@@ -3232,6 +3233,14 @@
"node": ">=8"
}
},
+ "node_modules/history": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/history/-/history-5.3.0.tgz",
+ "integrity": "sha512-ZqaKwjjrAYUYfLG+htGaIIZ4nioX2L70ZUMIFysS3xvBsSG4x/n1V6TXV3N8ZYNuFGlDirFg32T7B6WOUPDYcQ==",
+ "dependencies": {
+ "@babel/runtime": "^7.7.6"
+ }
+ },
"node_modules/hoist-non-react-statics": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
@@ -8699,6 +8708,14 @@
"resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz",
"integrity": "sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g=="
},
+ "history": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/history/-/history-5.3.0.tgz",
+ "integrity": "sha512-ZqaKwjjrAYUYfLG+htGaIIZ4nioX2L70ZUMIFysS3xvBsSG4x/n1V6TXV3N8ZYNuFGlDirFg32T7B6WOUPDYcQ==",
+ "requires": {
+ "@babel/runtime": "^7.7.6"
+ }
+ },
"hoist-non-react-statics": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
diff --git a/package.json b/package.json
index 614537ff..8d5a7fad 100644
--- a/package.json
+++ b/package.json
@@ -7,6 +7,7 @@
"axios": "^1.6.8",
"cors": "^2.8.5",
"express": "^4.19.2",
+ "history": "^5.3.0",
"jest": "^29.7.0",
"supertest": "^6.3.4"
},
diff --git a/resources/WIQ 2024 ES2B.gif b/resources/WIQ 2024 ES2B.gif
new file mode 100644
index 00000000..c933c761
Binary files /dev/null and b/resources/WIQ 2024 ES2B.gif differ
diff --git a/resources/wiq2024.png b/resources/wiq2024.png
new file mode 100644
index 00000000..84a1357c
Binary files /dev/null and b/resources/wiq2024.png differ
diff --git a/webapp/public/winners.png b/webapp/public/winners.png
new file mode 100644
index 00000000..f9a68b39
Binary files /dev/null and b/webapp/public/winners.png differ
diff --git a/webapp/src/App.css b/webapp/src/App.css
index 6524432d..0ac53da8 100644
--- a/webapp/src/App.css
+++ b/webapp/src/App.css
@@ -45,3 +45,11 @@
}
+h1{
+ color: #4C8DBF;
+ font-size: 2rem;
+ font-weight: 900;
+ text-decoration: none;
+}
+
+
diff --git a/webapp/src/App.js b/webapp/src/App.js
index c6f85bc3..87247327 100644
--- a/webapp/src/App.js
+++ b/webapp/src/App.js
@@ -21,19 +21,26 @@ function App() {
-
- Bienvenido a WIQ 2024 del curso de Arquitectura del Software
+
+
+ Bienvenido a
+
+ WIQ 2024
+
+
+
{showLogin ? : }
+
{showLogin ? (
-
+
¿No tienes una cuenta? Regístrate aquí.
-
+
) : (
-
+
¿Ya tienes cuenta? Inicia sesión aquí.
-
+
)}
diff --git a/webapp/src/App.test.js b/webapp/src/App.test.js
index 8602a898..6dbfc0d2 100644
--- a/webapp/src/App.test.js
+++ b/webapp/src/App.test.js
@@ -8,8 +8,12 @@ test('renders learn react link', () => {
);
- const linkElement = screen.getByText(/Bienvenido a WIQ 2024 del curso de Arquitectura del Software/i);
- expect(linkElement).toBeInTheDocument();
+ const welcomeText = screen.getByText(/Bienvenido a/i);
+
+ const wiqText = screen.getByText(/WIQ 2024/i);
+
+ expect(welcomeText).toBeInTheDocument();
+ expect(wiqText).toBeInTheDocument();
});
diff --git a/webapp/src/components/AddUser.js b/webapp/src/components/AddUser.js
index de0ffe5a..d3c835d5 100644
--- a/webapp/src/components/AddUser.js
+++ b/webapp/src/components/AddUser.js
@@ -1,30 +1,32 @@
-// src/components/AddUser.js
import React, { useState } from 'react';
import axios from 'axios';
import { Container, Typography, TextField, Button, Snackbar } from '@mui/material';
+import { useNavigate } from 'react-router-dom';
const apiEndpoint = process.env.REACT_APP_API_ENDPOINT || 'http://localhost:8000';
-const AddUser = () => {
+const AddUser = ({ onCloseSnackbar }) => {
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const [error, setError] = useState('');
const [openSnackbar, setOpenSnackbar] = useState(false);
+ const navigate = useNavigate();
const addUser = async () => {
try {
await axios.post(`${apiEndpoint}/adduser`, { username, password });
+ await axios.post(`${apiEndpoint}/login`, { username, password });
+ localStorage.setItem('username', username);
setOpenSnackbar(true);
-
+
+ // Redirige a la página de juego después de 3 segundos
+ navigate("/Game");
} catch (error) {
- setError(error.response.data.error);
+ setError("Error al crear usuario");
+ setOpenSnackbar(true); // Abre el Snackbar en caso de error
}
};
- const handleCloseSnackbar = () => {
- setOpenSnackbar(false);
- };
-
return (
@@ -34,7 +36,7 @@ const AddUser = () => {
name="username"
margin="normal"
fullWidth
- label="Username"
+ label="Nombre de usuario"
value={username}
onChange={(e) => setUsername(e.target.value)}
/>
@@ -42,7 +44,7 @@ const AddUser = () => {
name="password"
margin="normal"
fullWidth
- label="Password"
+ label="Contraseña"
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
@@ -50,7 +52,7 @@ const AddUser = () => {
-
+
{error && (
setError('')} message={`Error: ${error}`} />
)}
diff --git a/webapp/src/components/AddUser.test.js b/webapp/src/components/AddUser.test.js
index f9c55b41..4eaa9d2b 100644
--- a/webapp/src/components/AddUser.test.js
+++ b/webapp/src/components/AddUser.test.js
@@ -3,16 +3,21 @@ import { render, fireEvent, screen, waitFor } from '@testing-library/react';
import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
import AddUser from './AddUser';
+import { BrowserRouter } from 'react-router-dom';
const mockAxios = new MockAdapter(axios);
const renderAddUserComponent = () => {
- render();
+ return render(
+
+
+
+ );
};
const addUser = async () => {
- const usernameInput = screen.getByLabelText(/Username/i);
- const passwordInput = screen.getByLabelText(/Password/i);
+ const usernameInput = screen.getByLabelText(/Nombre de usuario/i);
+ const passwordInput = screen.getByLabelText(/Contraseña/i);
const addUserButton = screen.getByRole('button', { name: /Crear usuario/i });
fireEvent.change(usernameInput, { target: { value: 'testUser' } });
@@ -35,7 +40,7 @@ describe('AddUser component', () => {
await addUser();
await waitFor(() => {
- expect(screen.getByText(/User added successfully/i)).toBeInTheDocument();
+ expect(screen.getByText(/Usuario añadido correctamente/i)).toBeInTheDocument();
});
});
@@ -47,15 +52,15 @@ describe('AddUser component', () => {
await addUser();
await waitFor(() => {
- expect(screen.getByText(/Error: Internal Server Error/i)).toBeInTheDocument();
+ expect(screen.getByText(/Error: Error al crear usuario/i)).toBeInTheDocument();
});
});
it('should display proper labels and inputs', () => {
renderAddUserComponent();
- expect(screen.getByLabelText(/Username/i)).toBeInTheDocument();
- expect(screen.getByLabelText(/Password/i)).toBeInTheDocument();
+ expect(screen.getByLabelText(/Nombre de usuario/i)).toBeInTheDocument();
+ expect(screen.getByLabelText(/Contraseña/i)).toBeInTheDocument();
expect(screen.getByRole('button', { name: /Crear usuario/i })).toBeInTheDocument();
});
@@ -68,10 +73,10 @@ describe('AddUser component', () => {
jest.runAllTimers();
- expect(screen.queryByText(/User added successfully/i)).toBeNull();
+ expect(screen.queryByText(/Usuario añadido correctamente/i)).toBeNull();
await waitFor(() => {
- expect(screen.getByText(/User added successfully/i)).toBeInTheDocument();
+ expect(screen.getByText(/Usuario añadido correctamente/i)).toBeInTheDocument();
});
});
@@ -87,7 +92,7 @@ describe('AddUser component', () => {
expect(screen.queryByText(/Error: Internal Server Error/i)).toBeNull();
await waitFor(() => {
- expect(screen.getByText(/Error: Internal Server Error/i)).toBeInTheDocument();
+ expect(screen.getByText(/Error: Error al crear usuario/i)).toBeInTheDocument();
});
});
});
diff --git a/webapp/src/components/Footer.css b/webapp/src/components/Footer.css
index c64e75ff..a8c87eb3 100644
--- a/webapp/src/components/Footer.css
+++ b/webapp/src/components/Footer.css
@@ -1,12 +1,15 @@
.footer {
- position: absolute;
+ position: fixed;
width: 100%;
background-color: #f5f5f5;
padding: 0.5rem;
text-align: center;
- margin-top: 90vh;
- height: auto;
bottom: 0;
+ margin-top: 20rem;
+}
+
+.footer p{
+ font-size: 1rem;
}
.footer a {
@@ -17,4 +20,20 @@
.footer a:hover {
text-decoration: underline;
-}
\ No newline at end of file
+}
+
+.footer-wiq {
+ background-color: #333;
+ font-size: 1rem;
+ color: white;
+}
+
+.footer-text {
+ font-size: 1rem;
+}
+
+.footer-text a {
+ color: #4C8DBF;
+ font-weight: 700;
+ text-decoration: underline;
+}
diff --git a/webapp/src/components/Footer.js b/webapp/src/components/Footer.js
index 904473f0..8a4fb8b7 100644
--- a/webapp/src/components/Footer.js
+++ b/webapp/src/components/Footer.js
@@ -4,13 +4,13 @@ import { AppBar, Toolbar, Typography } from '@mui/material';
const Footer = () => {
return (
-
+
);
};
diff --git a/webapp/src/components/Game.js b/webapp/src/components/Game.js
index feab3620..9c3a1d27 100644
--- a/webapp/src/components/Game.js
+++ b/webapp/src/components/Game.js
@@ -325,7 +325,7 @@ const getQuestions = () => {
setGameData(newGame);
- axios.post(`${apiEndpoint}/addgame`, gameData)
+ axios.post(`${apiEndpoint}/addgame`, newGame)
.then(response => {
console.log("Respuesta del servidor:", response.data);
})
diff --git a/webapp/src/components/HistoricalData.css b/webapp/src/components/HistoricalData.css
index adba9ab8..35a4dbe3 100644
--- a/webapp/src/components/HistoricalData.css
+++ b/webapp/src/components/HistoricalData.css
@@ -30,7 +30,8 @@ table {
th, td {
padding: 0.25em;
text-align: center;
- border: 0.1em solid #000;
+ border: #000 solid 0.5rem;
+ font-weight: 600;
}
th[title='pregunta'] {
@@ -47,9 +48,5 @@ table {
td{
background-color: rgba(61, 178, 224, 0.764);
+ border: #000 solid 0.5rem;
}
-
-
-
-
-
diff --git a/webapp/src/components/HistoricalData.js b/webapp/src/components/HistoricalData.js
index fc6eaaa3..fa793d5b 100644
--- a/webapp/src/components/HistoricalData.js
+++ b/webapp/src/components/HistoricalData.js
@@ -14,7 +14,7 @@ const HistoricalData = () => {
useEffect(() => {
handleShowHistory();
// eslint-disable-next-line react-hooks/exhaustive-deps
- }, []); // No es necesario deshabilitar eslint, ya que no hay dependencias externas
+ }, []);
const handleShowHistory = async () => {
try {
@@ -31,7 +31,7 @@ const HistoricalData = () => {
const handleChangeRowsPerPage = (event) => {
setRowsPerPage(parseInt(event.target.value, 10));
- setPage(0); // Reiniciar a la primera página cuando cambia el número de filas por página
+ setPage(0);
};
@@ -59,12 +59,12 @@ const HistoricalData = () => {
{paginatedData.map((row, rowIndex) => (
-
- {row[0]}
- {row[1]}
- {row[2]}
- {row[3]}
- {row[4]}
+
+ {row[0]} |
+ {row[1]} |
+ {row[2]} |
+ {row[3]} |
+ {row[4]} |
))}
diff --git a/webapp/src/components/HistoricalData.test.js b/webapp/src/components/HistoricalData.test.js
index a8841eea..68b4b099 100644
--- a/webapp/src/components/HistoricalData.test.js
+++ b/webapp/src/components/HistoricalData.test.js
@@ -6,41 +6,35 @@ import HistoricalData from './HistoricalData';
import { BrowserRouter as Router } from 'react-router-dom';
const mockAxios = new MockAdapter(axios);
-
describe('HistoricalData component', () => {
- beforeEach(() => {
+ afterEach(() => {
mockAxios.reset();
});
it('muestra la página con el histórico de preguntas generadas', async () => {
-
const question1 = ['¿Cual es la capital de Venezuela?', 'Caracas', 'Doha', 'Barcelona', 'Nasáu'];
const question2 = ['¿Cual es la capital de Francia?', 'París', 'Londres', 'Madrid', 'Roma'];
const mockUsers = [question1, question2];
mockAxios.onGet("http://localhost:8000/getquestionshistory").reply(200, mockUsers);
render(
-
- );
+
+ );
await waitFor(() => {
-
- expect(screen.getByText('¿Cual es la capital de Venezuela?')).toBeInTheDocument();
- expect(screen.getByText('Caracas')).toBeInTheDocument();
- expect(screen.getByText('Doha')).toBeInTheDocument();
- expect(screen.getByText('Barcelona')).toBeInTheDocument();
- expect(screen.getByText('Nasáu')).toBeInTheDocument();
-
- expect(screen.getByText('¿Cual es la capital de Francia?')).toBeInTheDocument();
- expect(screen.getByText('París')).toBeInTheDocument();
- expect(screen.getByText('Londres')).toBeInTheDocument();
- expect(screen.getByText('Madrid')).toBeInTheDocument();
- expect(screen.getByText('Roma')).toBeInTheDocument();
-
- expect(screen.getByText('Rows per page:')).toBeInTheDocument();
-
- });
+ expect(screen.getByText('¿Cual es la capital de Venezuela?')).toBeInTheDocument();
+ expect(screen.getByText('Caracas')).toBeInTheDocument();
+ expect(screen.getByText('Doha')).toBeInTheDocument();
+ expect(screen.getByText('Barcelona')).toBeInTheDocument();
+ expect(screen.getByText('Nasáu')).toBeInTheDocument();
+
+ expect(screen.getByText('¿Cual es la capital de Francia?')).toBeInTheDocument();
+ expect(screen.getByText('París')).toBeInTheDocument();
+ expect(screen.getByText('Londres')).toBeInTheDocument();
+ expect(screen.getByText('Madrid')).toBeInTheDocument();
+ expect(screen.getByText('Roma')).toBeInTheDocument();
+
+ expect(screen.getByText('Rows per page:')).toBeInTheDocument();
+ });
});
});
-
-
diff --git a/webapp/src/components/HistoricalUserData.css b/webapp/src/components/HistoricalUserData.css
index f1a9f8aa..45a10ddd 100644
--- a/webapp/src/components/HistoricalUserData.css
+++ b/webapp/src/components/HistoricalUserData.css
@@ -16,7 +16,7 @@ div[title="botones"]{
button{
margin: 1em;
padding: 0.25em;
- background-color: rgba(31, 60, 134, 0.764);
+ background-color: #1f3c86c3;
color: white;
font-size: 0.75em;
}
@@ -30,7 +30,6 @@ table {
th, td {
padding: 0.25em;
text-align: center;
- border: 0.1em solid #000;
}
th[title='pregunta'] {
@@ -46,10 +45,12 @@ th[title='incorrecta'] {
}
td{
- background-color: rgba(61, 178, 224, 0.764);
+ background-color: #3db2e0c3;
}
-
-
+h2{
+ color: #1f3c86c3;
+ padding-bottom: 1rem;
+}
\ No newline at end of file
diff --git a/webapp/src/components/HistoricalUserData.js b/webapp/src/components/HistoricalUserData.js
index a42c3be9..dc03d750 100644
--- a/webapp/src/components/HistoricalUserData.js
+++ b/webapp/src/components/HistoricalUserData.js
@@ -90,13 +90,13 @@ const HistoricalUserData = () => {
{paginatedGameHistory.map((game, index) => (
-
- {formatDate(game.date)}
- {game.duration} segundos
- {game.percentage.toFixed(2)}%
- {game.totalQuestions}
- {game.correctAnswers}
- {game.incorrectAnswers}
+ |
+ {formatDate(game.date)} |
+ {game.duration} segundos |
+ {game.percentage.toFixed(2)}% |
+ {game.totalQuestions} |
+ {game.correctAnswers} |
+ {game.incorrectAnswers} |
{expandedRows.includes(index) && game.questions && game.questions.map((question, qIndex) => (
diff --git a/webapp/src/components/Login.js b/webapp/src/components/Login.js
index 084d4b25..76b7e870 100644
--- a/webapp/src/components/Login.js
+++ b/webapp/src/components/Login.js
@@ -5,7 +5,7 @@ import { useNavigate } from 'react-router-dom';
const Login = () => {
const apiEndpoint = process.env.REACT_APP_API_ENDPOINT || 'http://localhost:8000';
-
+
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const [error, setError] = useState('');
@@ -20,14 +20,10 @@ const Login = () => {
setOpenSnackbar(true);
navigate("/MainPage");
} catch (error) {
- setError(error.response.data.error);
+ setError('Error: Credenciales inválidas')
}
};
- const handleCloseSnackbar = () => {
- setOpenSnackbar(false);
- };
-
return (
@@ -37,14 +33,14 @@ const Login = () => {
setUsername(e.target.value)}
/>
setPassword(e.target.value)}
@@ -52,9 +48,9 @@ const Login = () => {
-
+ {setOpenSnackbar(false);}} message="Inicio de sesión exitoso" />
{error && (
- setError('')} message={`Error: ${error}`} />
+ {{setOpenSnackbar(true);setError('Error: Credenciales inválidas');}}} message={error} />
)}
diff --git a/webapp/src/components/Login.test.js b/webapp/src/components/Login.test.js
index 51963ada..7429b970 100644
--- a/webapp/src/components/Login.test.js
+++ b/webapp/src/components/Login.test.js
@@ -2,12 +2,19 @@ import React from 'react';
import { render, fireEvent, screen, waitFor, act } from '@testing-library/react';
import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
-import { BrowserRouter as Router } from "react-router-dom"; // No necesitas el alias aquí
+import { BrowserRouter as Router } from "react-router-dom";
import Login from './Login';
import { createMemoryHistory } from 'history';
const mockAxios = new MockAdapter(axios);
+function getLoginElements() {
+ const usernameInput = screen.getByLabelText(/Nombre de usuario/i);
+ const passwordInput = screen.getByLabelText(/Contraseña/i);
+ const loginButton = screen.getByRole('button', { name: /Iniciar sesión/i });
+ return { usernameInput, passwordInput, loginButton };
+}
+
describe('Login component', () => {
beforeEach(() => {
mockAxios.reset();
@@ -20,14 +27,10 @@ describe('Login component', () => {
);
- const usernameInput = screen.getByLabelText(/Username/i);
- const passwordInput = screen.getByLabelText(/Password/i);
- const loginButton = screen.getByRole('button', { name: /Iniciar sesión/i });
+ const { usernameInput, passwordInput, loginButton } = getLoginElements();
- // Mock the axios.post request to simulate a successful response
mockAxios.onPost('http://localhost:8000/login').reply(200, { createdAt: '2024-01-01T12:34:56Z' });
- // Simulate user input
await act(async () => {
fireEvent.change(usernameInput, { target: { value: 'testUser' } });
fireEvent.change(passwordInput, { target: { value: 'testPassword' } });
@@ -41,12 +44,11 @@ describe('Login component', () => {
);
- const usernameInput = screen.getByLabelText(/Username/i);
- const passwordInput = screen.getByLabelText(/Password/i);
- const loginButton = screen.getByRole('button', { name: /Iniciar sesión/i });
+ const { usernameInput, passwordInput, loginButton } = getLoginElements();
+
- // Mock the axios.post request to simulate an error response
- mockAxios.onPost('http://localhost:8000/login').reply(401, { error: 'Invalid credentials' });
+ // Mock del request axios.post para simular una respuesta de error
+ mockAxios.onPost('http://localhost:8000/login').reply(401, { error: 'Error: Credenciales inválidas' });
fireEvent.change(usernameInput, { target: { value: 'testUser' } });
fireEvent.change(passwordInput, { target: { value: 'testPassword' } });
@@ -54,7 +56,7 @@ describe('Login component', () => {
fireEvent.click(loginButton);
await waitFor(() => {
- expect(screen.getByText(/Error: Invalid credentials/i)).toBeInTheDocument();
+ expect(screen.getByText(/Error: Credenciales inválidas/i)).toBeInTheDocument();
});
});
@@ -66,11 +68,10 @@ describe('Login component', () => {
);
- const usernameInput = screen.getByLabelText(/Username/i);
- const passwordInput = screen.getByLabelText(/Password/i);
- const loginButton = screen.getByRole('button', { name: /Iniciar sesión/i });
+ const { usernameInput, passwordInput, loginButton } = getLoginElements();
- // Mock the axios.post request to simulate a successful response
+
+ // Mock del request axios.post para simular una respuesta exitosa
mockAxios.onPost('http://localhost:8000/login').reply(200, { createdAt: '2024-01-01T12:34:56Z' });
fireEvent.change(usernameInput, { target: { value: 'testUser' } });
@@ -79,10 +80,10 @@ describe('Login component', () => {
fireEvent.click(loginButton);
await waitFor(() => {
- expect(screen.getByText(/Login successful/i)).toBeInTheDocument();
+ expect(screen.getByText(/Inicio de sesión exitoso/i)).toBeInTheDocument();
});
- // Check if the redirection happens after the successful login
expect(history.location.pathname).toBe('/');
});
+
});
diff --git a/webapp/src/components/MainPage.css b/webapp/src/components/MainPage.css
index 58569311..97970849 100644
--- a/webapp/src/components/MainPage.css
+++ b/webapp/src/components/MainPage.css
@@ -12,8 +12,7 @@ div[title="main-title"]>h1 {
}
div[title="main-title"]>h2 {
- /* margin-bottom: 2rem; */
- color: #4c8dbf;
+ color: #0155B7;
font-size: 4rem;
font-weight: bold;
font-family: Verdana, Geneva, Tahoma, sans-serif;
@@ -58,7 +57,7 @@ div[title="main"]>button {
}
.img-container img {
- width: 70rem;
+ width: 70%;
height: auto;
animation: slide 3s ease infinite alternate;
}
@@ -88,4 +87,9 @@ div[title="main"]>button {
.dialogImage img {
width: 18rem;
+}
+
+
+.dialogContainer {
+ margin-bottom: 40%;
}
\ No newline at end of file
diff --git a/webapp/src/components/MainPage.js b/webapp/src/components/MainPage.js
index d444c689..bad92bca 100644
--- a/webapp/src/components/MainPage.js
+++ b/webapp/src/components/MainPage.js
@@ -1,6 +1,6 @@
// MainPage.js
import React, { createContext, useContext, useState } from 'react';
-import { Container, Typography, Button, Grid, Dialog, DialogTitle, DialogContent, TextField, DialogActions } from '@mui/material';
+import { Container, Typography, Button, Grid, Dialog, DialogTitle, DialogContent, TextField, DialogActions, Snackbar } from '@mui/material';
import { useNavigate } from 'react-router-dom';
import './MainPage.css';
@@ -19,25 +19,18 @@ const MainPage = () => {
const [open, setOpen] = useState(false);
const [numQuestions, setNumQuestions] = useState(5);
const [timePerQuestion, setTimePerQuestion] = useState(10);
-
- const handleNumQuestionsChange = (event) => {
- setNumQuestions(event.target.value);
- };
-
- const handleTimePerQuestionChange = (event) => {
- setTimePerQuestion(event.target.value);
- };
-
- const handleOpenDialog = () => {
- setOpen(true);
- };
+ const [error, setError] = useState('');
+ const [openSnackbar, setOpenSnackbar] = useState(false);
const handleCloseDialog = () => {
- setOpen(false);
- };
+ // Validar que el valor de preguntas sea al menos 5 y el tiempo por pregunta sea al menos 10
+ if (numQuestions < 5 || timePerQuestion < 10) {
+ setError('El número de preguntas debe ser al menos 5 y el tiempo por pregunta debe ser al menos 10 segundos.');
+ setOpenSnackbar(true);
+ return;
+ }
- const handleInputChange = (event) => {
- event.preventDefault();
+ setOpen(false);
};
const handleShowGame = () => {
@@ -52,11 +45,6 @@ const MainPage = () => {
navigate(path, { state: { gameConfig } });
};
- const handleRanking = () => {
- let path = '/ScoreBoard';
- navigate(path);
- };
-
// Valor del contexto para la configuración del juego
const configValue = {
numQuestions,
@@ -65,6 +53,7 @@ const MainPage = () => {
updateTimePerQuestion: setTimePerQuestion,
};
+
return (
@@ -83,7 +72,7 @@ const MainPage = () => {
-
+
@@ -91,10 +80,10 @@ const MainPage = () => {
-
@@ -120,8 +109,11 @@ const MainPage = () => {
type="number"
fullWidth
value={numQuestions}
- onChange={handleNumQuestionsChange}
- inputProps={{ min: 5, onKeyDown: handleInputChange }}
+ onChange={(event) => {
+ let newValue = parseInt(event.target.value, 10);
+ setNumQuestions(newValue);
+ }}
+ inputProps={{ min: 5 }}
className="dialogTextField"
/>
@@ -133,8 +125,12 @@ const MainPage = () => {
type="number"
fullWidth
value={timePerQuestion}
- onChange={handleTimePerQuestionChange}
- inputProps={{ min: 10, onKeyDown: handleInputChange }}
+ onChange={(event) => {
+ let newValue = parseInt(event.target.value, 10);
+ setTimePerQuestion(newValue);
+
+ }}
+ inputProps={{ min: 10 }}
className="dialogTextField"
/>
@@ -144,6 +140,13 @@ const MainPage = () => {
+
+
diff --git a/webapp/src/components/MainPage.test.js b/webapp/src/components/MainPage.test.js
index d6a71bd3..ee44fca2 100644
--- a/webapp/src/components/MainPage.test.js
+++ b/webapp/src/components/MainPage.test.js
@@ -1,5 +1,5 @@
import React from 'react';
-import { render, fireEvent, screen, act } from '@testing-library/react';
+import { render, fireEvent, screen, act, waitFor } from '@testing-library/react';
import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
import MainPage from './MainPage';
@@ -7,54 +7,121 @@ import { BrowserRouter as Router } from 'react-router-dom';
const mockAxios = new MockAdapter(axios);
+const renderMainPageComponent = () => {
+ return render(
+
+
+
+ );
+};
+
describe('MainPage component', () => {
beforeEach(() => {
mockAxios.reset();
});
it('muestra la página principal correctamente', async () => {
-
- render(
-
- );
-
+ renderMainPageComponent();
+
const element1 = screen.getByText(/¡Bienvenido a/);
const element2 = screen.getByText(/WIQ 2024!/);
const newGameButton = screen.getByRole('button', { name: 'Nuevo juego' });
- const rankingButton = screen.getByRole('button', { name: 'Ranking' });
- const configButton = screen.getByRole('button', { name: 'Configuración' });
-
+
expect(element1).toBeInTheDocument();
expect(element2).toBeInTheDocument();
-
+
+ fireEvent.click(newGameButton);
+
await act(async () => {
- fireEvent.click(newGameButton);
- });
-
+ });
expect(window.location.pathname).toBe('/Game');
+ });
- fireEvent.click(rankingButton);
+ it('abre el diálogo de configuración y cierra correctamente', async () => {
+ renderMainPageComponent();
+
+ const configButton = screen.getByRole('button', { name: 'Configuración' });
+
+ fireEvent.click(configButton);
+
+ const numQuestionsInput = screen.getByLabelText('Número de preguntas (min. 5)');
+ const timePerQuestionInput = screen.getByLabelText('Tiempo por pregunta (mín. 10 segundos)');
+ const acceptButton = screen.getByRole('button', { name: 'Aceptar' });
+
+ // Verificar que el diálogo se abre correctamente
+ expect(numQuestionsInput).toBeInTheDocument();
+ expect(timePerQuestionInput).toBeInTheDocument();
- expect(window.location.pathname).toBe('/ScoreBoard');
+
+ // Simular el cambio de valor en los campos de entrada
+ fireEvent.change(numQuestionsInput, { target: { value: '15' } });
+ fireEvent.change(timePerQuestionInput, { target: { value: '20' } });
+ fireEvent.click(acceptButton);
fireEvent.click(configButton);
+
+ // Verificar que los valores se actualizan correctamente
+ expect(numQuestionsInput.value).toBe('15');
+ expect(timePerQuestionInput.value).toBe('20');
+
+ });
- const dialogTitle = screen.getByText('Configuración del juego');
- expect(dialogTitle).toBeInTheDocument();
-
+
+ it('muestra un mensaje de error si las condiciones no se cumplen', async () => {
+ renderMainPageComponent();
+
+ const configButton = screen.getByRole('button', { name: 'Configuración' });
+
+ fireEvent.click(configButton);
+
const numQuestionsInput = screen.getByLabelText('Número de preguntas (min. 5)');
const timePerQuestionInput = screen.getByLabelText('Tiempo por pregunta (mín. 10 segundos)');
const acceptButton = screen.getByRole('button', { name: 'Aceptar' });
+
+ // Simular el cambio de valor en los campos de entrada con valores inválidos
+ fireEvent.change(numQuestionsInput, { target: { value: '3' } });
+ fireEvent.change(timePerQuestionInput, { target: { value: '5' } });
+
+ // Simular hacer clic en el botón de aceptar
+ fireEvent.click(acceptButton);
+
+ // Verificar que se muestra un mensaje de error
+ await waitFor(() => {
+ expect(screen.getByText(/El número de preguntas debe ser al menos 5 y el tiempo por pregunta debe ser al menos 10 segundos./i)).toBeInTheDocument();
+ });
+
+ // Verificar que el diálogo no se cierra
+ expect(screen.queryByText('Configuración del juego')).toBeInTheDocument();
+ });
+
- expect(dialogTitle).toBeVisible();
- expect(numQuestionsInput).toBeInTheDocument();
- expect(timePerQuestionInput).toBeInTheDocument();
- expect(acceptButton).toBeInTheDocument();
+ it('navega a la página de ScoreBoard al hacer clic en el botón "Ranking"', async () => {
+ renderMainPageComponent();
+
+ const rankingButton = screen.getByRole('button', { name: 'Ranking' });
+
+ fireEvent.click(rankingButton);
+
+ // Verificar que la ruta se cambió correctamente
+ await waitFor(() => {
+ expect(window.location.pathname).toBe('/ScoreBoard');
+ });
+ });
- fireEvent.change(numQuestionsInput, { target: { value: '10' } });
- fireEvent.change(timePerQuestionInput, { target: { value: '15' } });
- fireEvent.click(acceptButton);
+
+ it('navega a la página de Juego al hacer clic en el botón "Nuevo juego"', async () => {
+ renderMainPageComponent();
+
+ const gameButton = screen.getByRole('button', { name: 'Nuevo juego' });
+
+ fireEvent.click(gameButton);
+
+ // Verificar que la ruta se cambió correctamente
+ await waitFor(() => {
+ expect(window.location.pathname).toBe('/Game');
+ });
});
+
});
diff --git a/webapp/src/components/Navbar.css b/webapp/src/components/Navbar.css
index 8323e580..af45e72f 100644
--- a/webapp/src/components/Navbar.css
+++ b/webapp/src/components/Navbar.css
@@ -1,25 +1,44 @@
-.nav-link {
- color: #4c8dbf;
- font-weight: 400;
+.navbar {
+ background-color: white;
+ color: #333;
}
-.nav-link:hover {
- color: #BCC3C5;
- font-weight: 400;
+.navbar-brand {
+ color: #333;
}
-.navbar-brand {
- color: #0c3667;
- font-weight: 900;
- font-size: 2rem;
+.navbar-nav .nav-link {
+ color: #333;
+ font-weight: 700;
+ font-size: 1.3rem;
+ margin-right: 1rem;
}
-nav, .dropdown-menu, .dropdown-item {
- background-color: #4c8dbf;
- border: none;
+
+.navbar-nav .nav-link:hover {
+ color: #4C8DBF;
}
-.dropdown-item {
- color: #4c8dbf;
+.dropdown-menu {
+ background-color: #333;
}
+
+.dropdown-menu .dropdown-item {
+ color: #333;
+ font-weight: 700;
+ font-size: 1.1rem;
+
+}
+
+.dropdown-menu .dropdown-item:hover {
+ background-color: #969696;
+}
+
+.navbar-brd {
+ color: #4C8DBF;
+ text-decoration: none;
+ font-weight: 900;
+ font-size: 2rem;
+ margin-right: 1rem;
+}
\ No newline at end of file
diff --git a/webapp/src/components/Navbar.js b/webapp/src/components/Navbar.js
index 822b4b64..6f337078 100644
--- a/webapp/src/components/Navbar.js
+++ b/webapp/src/components/Navbar.js
@@ -18,7 +18,7 @@ const Navbar = () => {
return (