}
- {showComponent === 'userList' && }
- {showComponent === 'questionList' && }
- {showComponent === 'recordList' && }
- {showComponent === 'rankingList' && }
+ {showComponent === 'userList' && }
+ {showComponent === 'questionList' && }
+ {showComponent === 'recordList' && }
+ {showComponent === 'rankingList' && }
{showComponent === 'settings' && }
{showComponent === 'login' && (
diff --git a/webapp/src/components/Login.test.js b/webapp/src/components/Login.test.js
index f1aa7841..a4bea4ac 100644
--- a/webapp/src/components/Login.test.js
+++ b/webapp/src/components/Login.test.js
@@ -1,7 +1,13 @@
// Import necessary dependencies
import React from 'react';
-import { render, screen } from '@testing-library/react';
+import { render, screen, act, fireEvent, waitFor } from '@testing-library/react';
import Login from './Login';
+import axios from 'axios';
+import MockAdapter from 'axios-mock-adapter';
+
+jest.mock('axios');
+
+const mockAxios = new MockAdapter(axios);
// Define the test suite
describe('Login Component', () => {
@@ -14,4 +20,189 @@ describe('Login Component', () => {
const loginButton = screen.getByRole('button', { name: /Iniciar sesión/i });
expect(loginButton).toBeInTheDocument();
});
+
+ function setupMocksSuccess() {
+ const setLogged = jest.fn();
+
+ // Mock para la petición POST de login exitosa
+ axios.post.mockResolvedValueOnce({
+ data: {
+ createdAt: new Date().toISOString()
+ }
+ });
+
+ // Mock para la petición GET de obtener todos los usuarios
+ axios.get.mockResolvedValueOnce({
+ data: [] // Puedes ajustar esto según lo que necesites en tu test
+ });
+
+ // Mock para la petición POST de createUserRank exitosa
+ axios.post.mockResolvedValueOnce({
+ data: {} // Puedes ajustar esto según lo que necesites en tu test
+ });
+
+ return setLogged;
+ }
+
+ async function loginAndSearch(setLogged, username, password, search=true, all = false){
+ await act(async () => {
+ render();
+ });
+
+ const usernameInput = screen.getByLabelText(/Username/i);
+ const passwordInput = screen.getByLabelText(/Password/i);
+ const loginButton = screen.getByRole('button', { name: /Iniciar sesión/i });
+
+ await act(async () => {
+ fireEvent.change(usernameInput, { target: { value: username } });
+ fireEvent.change(passwordInput, { target: { value: password } });
+
+ fireEvent.click(loginButton);
+ });
+
+ if(search){
+ await waitFor(() => {
+ expect(setLogged).toHaveBeenCalledTimes(1);
+
+ expect(screen.getAllByText(/Jugar/i)).toHaveLength(2);
+ expect(screen.getByText(/Historial de jugadas/i)).toBeInTheDocument();
+ expect(screen.getByText(/Ranking/i)).toBeInTheDocument();
+ expect(screen.getByText(/Ajustes de partida/i)).toBeInTheDocument();
+
+ expect(screen.getByText(new RegExp(`Hola ${username}!`, 'i'))).toBeInTheDocument();
+ expect(screen.getByText(/Tu cuenta fue creada el/i)).toBeInTheDocument();
+ expect(screen.getByText(/Comenzar a jugar/i)).toBeInTheDocument();
+
+ if(all){ // only for admin
+ expect(screen.getByText(/Historial de Usuarios/i)).toBeInTheDocument();
+ expect(screen.getByText(/Historial de Preguntas Generadas/i)).toBeInTheDocument();
+ }
+ });
+ }
+ }
+
+ test('login with valid normal (not "admin") credentials', async () => {
+ const setLogged = setupMocksSuccess();
+
+ await loginAndSearch(setLogged, 'testUser', 'testPassword');
+ });
+
+ test('login with valid admin credentials', async () => {
+ const setLogged = setupMocksSuccess();
+
+ await loginAndSearch(setLogged, 'admin', 'testPassword');
+ });
+
+ describe('sucessful login cases trying to access to: userList, questionList, recordList, rankingList, settings', () => {
+ beforeEach(async () => {
+ const setLogged = jest.fn();
+
+ axios.post.mockResolvedValueOnce({
+ data: {
+ createdAt: new Date().toISOString()
+ }
+ });
+
+ axios.get.mockResolvedValueOnce({
+ data: []
+ });
+
+ axios.post.mockResolvedValueOnce({
+ data: {}
+ });
+
+ await loginAndSearch(setLogged, 'admin', 'testPassword', true, true);
+ });
+
+ async function accessToTab(tabName, tabText){
+ const tab = screen.getByText(new RegExp(tabName, 'i'));
+ await act(async () => {
+ fireEvent.click(tab);
+ });
+
+ await waitFor(async () => {
+ expect((await screen.findAllByText(new RegExp(tabText, 'i'))).length).toBeGreaterThan(0);
+ });
+ }
+
+ test('from login try to access to usersList', async () => {
+ await accessToTab('Historial de Usuarios', 'Nombre de Usuario');
+ });
+
+ test('from login try to access to generatedQuestionsList', async () => {
+ await accessToTab('Historial de Preguntas Generadas', 'Lista de preguntas');
+ });
+
+ test('from login try to access to recordList', async () => {
+ await accessToTab('Historial de jugadas', 'Tu historial de jugadas');
+ });
+
+ test('from login try to access to rankingList', async () => {
+ await accessToTab('Ranking', 'Ranking');
+ });
+
+ test('from login try to access to gameSettings', async () => {
+ await accessToTab('Ajustes de partida', 'Número de preguntas');
+ });
+
+ });
+
+ async function performLoginFail(setLogged, username = 'testUser', password = 'testPassword', error = 'Internal Server Error', loggedIn = false) {
+ await loginAndSearch(setLogged, username, password, false);
+
+ await waitFor(() => {
+ if (!loggedIn) {
+ expect(setLogged).not.toHaveBeenCalled();
+ } else {
+ expect(setLogged).toHaveBeenCalled();
+ }
+ expect(screen.getByText(new RegExp(error, 'i'))).toBeInTheDocument();
+ expect(screen.queryByText(/Comenzar a jugar/i)).not.toBeInTheDocument();
+ });
+ }
+
+ test('login fails on post /login and error is handled ', async () => {
+ const setLogged = jest.fn();
+
+ // Mock para la petición POST de login fallada
+ axios.post.mockRejectedValueOnce({ response: { status: 500, data: { error: 'Internal Server Error' } } });
+
+ await performLoginFail(setLogged);
+ });
+
+ test('login fails on get /getAllUsers and error is handled ', async () => {
+ const setLogged = jest.fn();
+
+ // Mock para la petición POST de login exitosa
+ axios.post.mockResolvedValueOnce({
+ data: {
+ createdAt: new Date().toISOString()
+ }
+ });
+ // Mock para la petición get de login fallada
+ axios.get.mockRejectedValueOnce({ response: { status: 500, data: { error: 'Internal Server Error' } } });
+
+ await performLoginFail(setLogged);
+ });
+
+ test('login fails on post /createUserRank and error is handled ', async () => {
+ const setLogged = jest.fn();
+
+ // Mock para la petición POST de login exitosa
+ axios.post.mockResolvedValueOnce({
+ data: {
+ createdAt: new Date().toISOString()
+ }
+ });
+
+ // Mock para la petición GET de obtener todos los usuarios
+ axios.get.mockResolvedValueOnce({
+ data: [] // Puedes ajustar esto según lo que necesites en tu test
+ });
+
+ // Mock para la petición POST de login fallada
+ axios.post.mockRejectedValueOnce({ response: { status: 500, data: { error: 'Internal Server Error' } } });
+
+ await performLoginFail(setLogged, 'testUser', 'testPassword', 'Espere, estamos cargando sus datos...', true);
+ });
});
diff --git a/webapp/src/components/RankingList.js b/webapp/src/components/RankingList.js
index ee9918f0..7ba00ec9 100644
--- a/webapp/src/components/RankingList.js
+++ b/webapp/src/components/RankingList.js
@@ -1,7 +1,8 @@
import React, { useState, useEffect } from 'react';
import axios from 'axios';
+import PropTypes from 'prop-types';
-const RankingList = () => {
+const RankingList = ({setError}) => {
const [listUsers, setListUsers] = useState([]);
const [sortColumn, setSortColumn] = useState('porcentajeAciertos');
const [sortOrder, setSortOrder] = useState('desc');
@@ -23,10 +24,10 @@ const RankingList = () => {
const sortedUsers = [...uList].sort((a, b) => b.porcentajeAciertos - a.porcentajeAciertos);
setTopThreeUsers(sortedUsers.slice(0, 3));
} else {
- console.error('Error obteniendo la lista de usuarios');
+ setError('Error obteniendo el ranking del usuario');
}
} catch (error) {
- console.error('Error obteniendo la lista de usuarios:', error);
+ setError(`Error obteniendo el ranking del usuario: ${error}`);
}
};
@@ -99,4 +100,9 @@ const RankingList = () => {
);
};
+
+RankingList.propTypes = {
+ setError: PropTypes.func.isRequired,
+};
+
export default RankingList;
diff --git a/webapp/src/components/RankingList.test.js b/webapp/src/components/RankingList.test.js
index 0837377d..0dbbd0c2 100644
--- a/webapp/src/components/RankingList.test.js
+++ b/webapp/src/components/RankingList.test.js
@@ -2,9 +2,12 @@ import React from 'react';
import { render, screen, waitFor, act } from '@testing-library/react';
import RankingList from './RankingList';
import axios from 'axios';
+import MockAdapter from 'axios-mock-adapter';
jest.mock('axios');
+const mockAxios = new MockAdapter(axios);
+
describe('RankingList', () => {
describe('successful requests', () => {
beforeEach(() => {
@@ -43,17 +46,23 @@ describe('RankingList', () => {
});
});
- it('renders without crashing', async () => {
+ function emptyFunction() {
+ return;
+ }
+
+ async function renderRankingList() {
await act(async () => {
- render();
+ render();
});
+ }
+
+ it('renders without crashing', async () => {
+ await renderRankingList();
});
test('renders RankingList component and main heading', async () => {
- await act(async () => {
- render();
- });
+ await renderRankingList();
// Check if the main heading is in the document
const heading = screen.getByRole('heading', { name: /Top 3 usuarios con mejor porcentaje de aciertos/i });
@@ -62,9 +71,7 @@ describe('RankingList', () => {
// Test for rendering the column headers
test('renders column headers', async () => {
- await act(async () => {
- render();
- });
+ await renderRankingList();
// Check if the column headers are in the document
const columnHeaders = screen.getAllByRole('columnheader');
@@ -73,9 +80,7 @@ describe('RankingList', () => {
// Test for rendering the table
it('should display the table', async () => {
- await act(async () => {
- render();
- });
+ await renderRankingList();
const table = screen.getByRole('table');
expect(table).toBeInTheDocument();
@@ -83,9 +88,7 @@ describe('RankingList', () => {
test('tests tabla ranking', async () => {
- await act(async () => {
- render();
- });
+ await renderRankingList();
expect(screen.queryByText("Ranking")).toBeInTheDocument();
expect(screen.getByText(/Nombre de Usuario/i)).toBeInTheDocument();
@@ -96,18 +99,14 @@ describe('RankingList', () => {
});
test('show ranking table with content', async () => {
- await act(async () => {
- render();
- });
+ await renderRankingList();
const rows = await screen.findAllByRole('row');
expect(rows).toHaveLength(5);
});
test('show users ordered by "porcentajeAciertos" BY DEFAULT correctly', async () => {
- await act(async () => {
- render();
- });
+ await renderRankingList();
// We wait for the users to be loaded and the table to be updated
let rows = await screen.findAllByRole('row');
@@ -120,10 +119,40 @@ describe('RankingList', () => {
});
- test('show users ordered by "porcentajeAciertos" correctly', async () => {
- await act(async () => {
- render();
+ test('show users ordered by "username" correctly', async () => {
+ await renderRankingList();
+ const usernameHeader = screen.getByRole('columnheader', { name: /Nombre de Usuario/i });
+
+ await act(async() => {
+ usernameHeader.click(); // DESC
});
+
+ // We wait for the users to be loaded and the table to be updated
+ let rows = await screen.findAllByRole('row');
+
+ // We check if the first row is the one with the username 'troll'
+ expect(rows[4]).toHaveTextContent('manuel');
+ expect(rows[3]).toHaveTextContent('maría');
+ expect(rows[2]).toHaveTextContent('pedro');
+ expect(rows[1]).toHaveTextContent('troll');
+
+ await act(async() => {
+ usernameHeader.click(); // ASC
+ });
+
+ // We wait for the users to be loaded and the table to be updated
+ rows = await screen.findAllByRole('row');
+
+ // We check if the first row is the one with the username 'manuel'
+ expect(rows[1]).toHaveTextContent('manuel');
+ expect(rows[2]).toHaveTextContent('maría');
+ expect(rows[3]).toHaveTextContent('pedro');
+ expect(rows[4]).toHaveTextContent('troll');
+
+ });
+
+ test('show users ordered by "porcentajeAciertos" correctly', async () => {
+ await renderRankingList();
const porcentajeAciertosHeader = screen.getByRole('columnheader', { name: /Porcentaje de Aciertos/i });
await act(async() => {
@@ -155,9 +184,7 @@ describe('RankingList', () => {
});
test('show users ordered by "preguntasCorrectas" correctly', async () => {
- await act(async () => {
- render();
- });
+ await renderRankingList();
const preguntasCorrectasHeader = screen.getByRole('columnheader', { name: /Preguntas Correctas/i });
await act(async() => {
@@ -188,9 +215,7 @@ describe('RankingList', () => {
});
test('show users ordered by "preguntasFalladas" correctly', async () => {
- await act(async () => {
- render();
- });
+ await renderRankingList();
const preguntasFalladasHeader = screen.getByRole('columnheader', { name: /Preguntas Falladas/i });
await act(async() => {
@@ -221,9 +246,7 @@ describe('RankingList', () => {
});
test('show users ordered by "numeroPartidas" correctly', async () => {
- await act(async () => {
- render();
- });
+ await renderRankingList();
const numPartidasHeader = screen.getByRole('columnheader', { name: /Número de Partidas/i });
await act(async() => {
@@ -255,4 +278,28 @@ describe('RankingList', () => {
}); // fin tests correctos
+ test('should display an error message when the request fails', async () => {
+ let errorShown = "";
+ await act(async () => {
+ render( {errorShown=errorMsg}} />);
+ });
+
+ // simulate a failed request
+ mockAxios.onPost('http://localhost:8000/obtainRank').reply(500, { error: 'Internal Server Error' });
+
+ // Check if the table headers are in the document
+ expect(screen.queryByText("Ranking")).toBeInTheDocument();
+ expect(screen.getByText(/Nombre de Usuario/i)).toBeInTheDocument();
+ expect(screen.queryAllByText(/Porcentaje de Aciertos/i)).not.toHaveLength(0);
+ expect(screen.getByText(/Preguntas Correctas/i)).toBeInTheDocument();
+ expect(screen.getByText(/Preguntas Falladas/i)).toBeInTheDocument();
+ expect(screen.getByText(/Número de Partidas/i)).toBeInTheDocument();
+
+ // and no users rows are shown
+ const rows = await screen.findAllByRole('row');
+ expect(rows.length).toBe(1);
+
+ expect(errorShown).toBe("Error obteniendo el ranking del usuario: TypeError: Cannot read properties of undefined (reading 'status')");
+ });
+
});
diff --git a/webapp/src/components/RecordList.js b/webapp/src/components/RecordList.js
index e7fee959..da88832f 100644
--- a/webapp/src/components/RecordList.js
+++ b/webapp/src/components/RecordList.js
@@ -1,7 +1,8 @@
import React, { useState, useEffect } from 'react';
import axios from 'axios';
+import PropTypes from 'prop-types';
-const RecordList = ({ username }) => {
+const RecordList = ({ username, setError }) => {
const [listRecords, setListRecords] = useState([]);
const apiEndpoint = process.env.REACT_APP_API_ENDPOINT || 'http://localhost:8000';
@@ -17,10 +18,10 @@ const RecordList = ({ username }) => {
}));
setListRecords(userRecords);
} else {
- console.error('Error obtaining the user records list');
+ setError('Error obtaining the user records list');
}
} catch (error) {
- console.error('Error obtaining the user records list:', error);
+ setError('Error obtaining the user records list: '+ error);
}
};
@@ -56,4 +57,9 @@ const RecordList = ({ username }) => {
);
};
+RecordList.propTypes = {
+ username: PropTypes.string.isRequired,
+ setError: PropTypes.func.isRequired,
+};
+
export default RecordList;
diff --git a/webapp/src/components/RecordList.test.js b/webapp/src/components/RecordList.test.js
index f7ea2c02..5945fd6d 100644
--- a/webapp/src/components/RecordList.test.js
+++ b/webapp/src/components/RecordList.test.js
@@ -29,7 +29,7 @@ describe('RecordList', () => {
});
it('renders record list correctly', async () => {
- const { getByText } = render();
+ const { getByText } = render( {}} username="testuser" />);
await waitFor(() => {
expect(getByText('Tu historial de jugadas')).toBeInTheDocument();
diff --git a/webapp/src/components/UsersList.js b/webapp/src/components/UsersList.js
index d3d6af00..aa9650af 100644
--- a/webapp/src/components/UsersList.js
+++ b/webapp/src/components/UsersList.js
@@ -1,9 +1,11 @@
import React, { useState, useEffect } from 'react';
import axios from 'axios';
+import PropTypes from 'prop-types';
+
//import { Container, Typography, TextField, Button, Snackbar } from '@mui/material';
-const UsersList = () => {
+const UsersList = ({ setError }) => {
const [listUsers, setListUsers] = useState([]);
@@ -25,13 +27,12 @@ const UsersList = () => {
setListUsers(uList);
} else {
- console.error('Error obteniendo la lista de usurios');
+ setError('Error obteniendo la lista de usurios');
}
} catch (error) {
- console.error('Error obteniendo la lista de usurios:', error);
+ setError(`Error obteniendo la lista de usurios: ${error}`);
}
};
-
fetchUsers();
}, [apiEndpoint]);
@@ -81,4 +82,8 @@ const UsersList = () => {
);
};
+UsersList.propTypes = {
+ setError: PropTypes.func.isRequired,
+};
+
export default UsersList;
\ No newline at end of file
diff --git a/webapp/src/components/UsersList.test.js b/webapp/src/components/UsersList.test.js
index def7d975..fb6573b6 100644
--- a/webapp/src/components/UsersList.test.js
+++ b/webapp/src/components/UsersList.test.js
@@ -34,9 +34,13 @@ describe('UsersList', () => {
});
});
+ function emptyFunction() {
+ return;
+ }
+
it('renders headers list correctly', async () => {
await act(async () => {
- render();
+ render();
});
// Check if the table headers are in the document
@@ -49,7 +53,7 @@ describe('UsersList', () => {
it('renders all the users rows', async () => {
await act(async () => {
- render();
+ render();
});
// Check if the table rows are in the document
const tableRows = screen.getAllByRole('row');
@@ -58,7 +62,7 @@ describe('UsersList', () => {
it('should order users by username correctly', async () => {
await act(async () => {
- render();
+ render();
});
// We click the username header to order the users by username
@@ -92,7 +96,7 @@ describe('UsersList', () => {
it('should order users by createdAt date correctly', async () => {
await act(async () => {
- render();
+ render();
});
// We click the username header to order the users by username
@@ -125,24 +129,19 @@ describe('UsersList', () => {
});
});
+ function errorFunction(errorMsg) {
+ expect(errorMsg).toBe("Error obteniendo la lista de usurios: TypeError: Cannot read properties of undefined (reading 'status')");
+ }
describe('failing requests', () => {
- beforeEach(() => {
- axios.get.mockRejectedValue({
- response: {
- status: 500,
- data: {
- error: 'Internal Server Error'
- },
- },
- });
- });
-
- it('users list is empty (only headers are shown) when petition fails', async () => {
+ test('users list is empty (only headers are shown) when petition fails', async () => {
await act(async () => {
- render();
+ render();
});
+ // simulate a failed request
+ mockAxios.onPost('http://localhost:8000/getAllUsers').reply(500, { error: 'Internal Server Error' });
+
// Check if the table headers are in the document
const usernameHeader = screen.getByRole('columnheader', { name: /Nombre de Usuario/i });
const createdAtHeader = screen.getByRole('columnheader', { name: /Fecha de Registro/i });