diff --git a/webapp/public/locales/en/translation.json b/webapp/public/locales/en/translation.json
index b61195dc..d6cdce40 100644
--- a/webapp/public/locales/en/translation.json
+++ b/webapp/public/locales/en/translation.json
@@ -53,10 +53,11 @@
}
},
"rules": {
- "description1": "The WIQ game consists of quick games of 9 rounds. In each round there is one question and two possible answers. The key to earning points lies in choosing the correct answer.",
- "description2": "There is only one correct answer.",
- "description3": "You have to select a question before time runs out.",
- "description4": "To start playing you have to click on the Play button."
+ "description1": "Welcome to the exciting world of KiWiQ! In this challenging game, your goal is to embark on a journey full of knowledge and fun.",
+ "description2": "Each question is a door to a universe of possibilities, with four options before you. But be careful, only one of those options is the golden key that will unlock the treasure of the correct answer.",
+ "description3": "With just 30 seconds for each question, you'll feel the adrenaline rushing through your veins as you fight against the clock to find the perfect answer. Take on the standard challenge with 9 questions waiting for you in each match, or venture into the custom terrain, where you can choose your own route. Do you feel brave? Or do you prefer strategy?",
+ "description4": "At the end of the road, your score will be your badge of honor. Can you achieve maximum glory and become the KiWiQ champion? Only time will tell!",
+ "description5": "Let the game begin!"
},
"statistics": {
diff --git a/webapp/public/locales/es/translation.json b/webapp/public/locales/es/translation.json
index a67bc786..3b5b5b90 100644
--- a/webapp/public/locales/es/translation.json
+++ b/webapp/public/locales/es/translation.json
@@ -53,10 +53,11 @@
}
},
"rules": {
- "description1": "El juego de WIQ consiste en juegos rápidos de 9 rondas. En cada ronda hay una pregunta y dos posibles respuestas. La clave para ganar puntos está en elegir la respuesta correcta.",
- "description2": "Solo hay una respuesta correcta.",
- "description3": "Debes seleccionar una pregunta antes de que se acabe el tiempo.",
- "description4": "Para comenzar a jugar, debes hacer clic en el botón Jugar."
+ "description1": "¡Bienvenidos al emocionante mundo de KiWiQ! En este desafiante juego, tu objetivo es embarcarte en un viaje lleno de conocimiento y diversión.",
+ "description2": "Cada pregunta es una puerta hacia un universo de posibilidades, con cuatro opciones ante ti. Pero cuidado, solo una de esas opciones es la clave dorada que desbloqueará el tesoro de la respuesta correcta.",
+ "description3": "Con solo 30 segundos para cada pregunta, sentirás la adrenalina corriendo por tus venas mientras luchas contra el reloj para encontrar la respuesta perfecta. Enfrenta el desafío estándar con 9 preguntas esperándote en cada partida, o aventúrate en el terreno personalizado, donde puedes elegir tu propia ruta. ¿Te sientes valiente? ¿O prefieres la estrategia?",
+ "description4": "Al final del camino, tu puntuación será tu insignia de honor. ¿Podrás alcanzar la gloria máxima y convertirte en el campeón de KiWiQ? ¡Solo el tiempo lo dirá!",
+ "description5": "¡Que comience el juego!"
},
"statistics": {
"position": "Posición",
diff --git a/webapp/src/components/statistics/UserStatistics.jsx b/webapp/src/components/statistics/UserStatistics.jsx
index f1b36b86..9f90eac7 100644
--- a/webapp/src/components/statistics/UserStatistics.jsx
+++ b/webapp/src/components/statistics/UserStatistics.jsx
@@ -2,33 +2,32 @@ import { Box, Flex, Heading, Stack, Text, CircularProgress } from "@chakra-ui/re
import { HttpStatusCode } from "axios";
import ErrorMessageAlert from "components/ErrorMessageAlert";
import AuthManager from "components/auth/AuthManager";
-import React, {useEffect, useState} from "react";
+import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { Cell, Pie, PieChart } from "recharts";
export default function UserStatistics() {
- const {t} = useTranslation();
+ const { t } = useTranslation();
const [userData, setUserData] = useState(null);
const [retrievedData, setRetrievedData] = useState(false);
const [errorMessage, setErrorMessage] = useState(null);
const getData = async () => {
try {
- const request = await new AuthManager().getAxiosInstance()
- .get(process.env.REACT_APP_API_ENDPOINT + "/statistics/personal");
+ const request = await new AuthManager().getAxiosInstance().get(process.env.REACT_APP_API_ENDPOINT + "/statistics/personal");
if (request.status === HttpStatusCode.Ok) {
setUserData({
- "raw": [
+ raw: [
{
- "name": t("statistics.texts.personalRight"),
- "value": request.data.right
+ name: t("statistics.texts.personalRight"),
+ value: request.data.right,
},
{
- "name": t("statistics.texts.personalWrong"),
- "value": request.data.wrong
- }
+ name: t("statistics.texts.personalWrong"),
+ value: request.data.wrong,
+ },
],
- "rate": request.data.correct_rate
+ rate: request.data.correct_rate
});
setRetrievedData(true);
} else {
@@ -38,67 +37,72 @@ export default function UserStatistics() {
let errorType;
switch (error.response ? error.response.status : null) {
case 400:
- errorType = { type: t("error.validation.type"), message: t("error.validation.message")};
+ errorType = { type: t("error.validation.type"), message: t("error.validation.message") };
break;
case 404:
- errorType = { type: t("error.notFound.type"), message: t("error.notFound.message")};
+ errorType = { type: t("error.notFound.type"), message: t("error.notFound.message") };
break;
default:
- errorType = { type: t("error.unknown.type"), message: t("error.unknown.message")};
+ errorType = { type: t("error.unknown.type"), message: t("error.unknown.message") };
break;
}
setErrorMessage(errorType);
}
- }
+ };
useEffect(() => {
- if(!retrievedData){
+ if (!retrievedData) {
getData();
}
});
- return
- {
- retrievedData ?
- <>
-
-
- {t("common.statistics.personal")}
-
-
- {t("statistics.rightAnswers")}
-
-
- {t("statistics.texts.personalRight", {right: userData.raw[0].value})}
-
-
-
-
+
+ return (
+
+ {retrievedData ? (
+
+
+
+ {t("common.statistics.personal")}
+
+
+
+
+
+ {t("statistics.rightAnswers")}
+
+
+ {t("statistics.texts.personalRight", { right: userData.raw[0].value })}
+
+
+
+
{t("statistics.wrongAnswers")}
-
-
- {t("statistics.texts.personalWrong", {wrong: userData.raw[1].value}) }
-
-
-
-
- {t("statistics.percentage")}
-
-
- {t("statistics.texts.personalRate", {rate: userData.rate})}
-
-
-
-
-
-
+
+
+ {t("statistics.texts.personalWrong", { wrong: userData.raw[1].value })}
+
+
+
+
+ {t("statistics.percentage")}
+
+
+ {t("statistics.texts.personalRate", { rate: userData.rate })}
+
+
+
+
+
+
|
|
-
-
- >
- :
- }
+
+
+
+
+ ) : (
+
+ )}
-}
\ No newline at end of file
+ );
+}
diff --git a/webapp/src/pages/Rules.jsx b/webapp/src/pages/Rules.jsx
index 7b528024..c8c33b60 100644
--- a/webapp/src/pages/Rules.jsx
+++ b/webapp/src/pages/Rules.jsx
@@ -23,8 +23,8 @@ export default function Rules() {
setIsMenuOpen(false)} changeLanguage={changeLanguage} isDashboard={false}/>
{t("common.rules")}
-
-
+
+
{t("rules.description1")}
{t("rules.description2")}
@@ -32,6 +32,8 @@ export default function Rules() {
{t("rules.description3")}
{t("rules.description4")}
+
+ {t("rules.description5")}
diff --git a/webapp/src/pages/Statistics.jsx b/webapp/src/pages/Statistics.jsx
index c59f32bc..0fcb86cd 100644
--- a/webapp/src/pages/Statistics.jsx
+++ b/webapp/src/pages/Statistics.jsx
@@ -18,7 +18,6 @@ export default function Statistics() {
const [errorMessage, setErrorMessage] = useState(null);
const getData = async () => {
- console.log('lmao')
try {
const request = await new AuthManager().getAxiosInstance()
.get(process.env.REACT_APP_API_ENDPOINT + "/statistics/top");
@@ -47,8 +46,8 @@ export default function Statistics() {
const formatTopTen = () => {
return topTen.map((element, counter) => {
- return
- {counter + 1} |
+ return
+ {counter + 1} |
{element.user.username} |
{element.right} |
{element.wrong} |
@@ -82,7 +81,7 @@ export default function Statistics() {
{t("common.statistics.title")}
} minH="50vh"
- p="1rem" backgroundColor="whiteAlpha.900" shadow="2xl"
+ p="1rem" backgroundColor="whiteAlpha.900" shadow="1.25em"
boxShadow="md" rounded="1rem" alignItems={"center"} data-testid={"leaderboard-component"}>
{retrievedData ?
@@ -92,15 +91,15 @@ export default function Statistics() {
{
topTen.length === 0 ?
{t("statistics.empty")} :
-
+
-
- {t("statistics.position")} |
- {t("statistics.username")} |
- {t("statistics.rightAnswers")} |
- {t("statistics.wrongAnswers")} |
- {t("statistics.totalAnswers")} |
- {t("statistics.percentage")} |
+
+ {t("statistics.position")} |
+ {t("statistics.username")} |
+ {t("statistics.rightAnswers")} |
+ {t("statistics.wrongAnswers")} |
+ {t("statistics.totalAnswers")} |
+ {t("statistics.percentage")} |
diff --git a/webapp/src/tests/LateralMenu.test.js b/webapp/src/tests/LateralMenu.test.js
index b5524ac1..fd5781e0 100644
--- a/webapp/src/tests/LateralMenu.test.js
+++ b/webapp/src/tests/LateralMenu.test.js
@@ -1,10 +1,11 @@
import React from 'react';
-import { render, screen, waitFor } from '@testing-library/react';
+import { render, screen, waitFor, fireEvent } from '@testing-library/react';
import { MemoryRouter } from 'react-router';
import { ChakraProvider } from '@chakra-ui/react';
import theme from '../styles/theme';
import AuthManager from '../components/auth/AuthManager';
import LateralMenu from '../components/LateralMenu';
+import userEvent from '@testing-library/user-event';
jest.mock('react-i18next', () => ({
useTranslation: () => {
@@ -116,9 +117,37 @@ describe('LateralMenu component', () => {
expect(logoutButton).toBeNull();
});
+ it('renders logout button when isLoggedIn is true', async () => {
+ authManager.setLoggedIn(true);
+ const { getByText } = render();
+ await waitFor(() => {
+ expect(getByText('common.logout')).toBeInTheDocument();
+ });
+ });
+
it('renders about button', () => {
render();
const aboutButton = screen.getByLabelText('About');
expect(aboutButton).toBeInTheDocument();
});
+ it('changes language on select change', async () => {
+ const changeLanguageMock = jest.fn();
+ render( {}} changeLanguage={changeLanguageMock} isDashboard={false} />);
+
+ userEvent.selectOptions(screen.getByTestId('language-select'), 'en');
+ await waitFor(() => {
+ expect(changeLanguageMock).toHaveBeenCalledWith('en');
+ });
+ });
+ it('renders API button when isLoggedIn is true', async () => {
+ authManager.setLoggedIn(true);
+ const { getByText } = render();
+ await waitFor(() => {
+ expect(getByText('API')).toBeInTheDocument();
+ });
+ fireEvent.click(screen.getByTestId('API'));
+ await waitFor(() => {
+ expect(screen.getByText('KIWIQ')).toBeInTheDocument();
+ });
+ });
});
diff --git a/webapp/src/tests/Statistics.test.js b/webapp/src/tests/Statistics.test.js
index a0402cdd..5f0e5134 100644
--- a/webapp/src/tests/Statistics.test.js
+++ b/webapp/src/tests/Statistics.test.js
@@ -105,6 +105,30 @@ describe("Statistics", () => {
});
});
+ test("renders initial loading state", () => {
+ render();
+ expect(screen.getByTestId("leaderboard-spinner")).toBeVisible();
+ });
+
+ test("displays error message when data retrieval fails", async () => {
+ const errorMessage = "error.unknown.typeerror.unknown.message";
+ const mockAxios = new MockAdapter(authManager.getAxiosInstance());
+ mockAxios.onGet().reply(HttpStatusCode.InternalServerError);
+ render();
+ await waitFor(() => {
+ expect(screen.getByTestId("error-message")).toHaveTextContent(errorMessage);
+ });
+ });
+
+ test("displays empty state when no data is returned", async () => {
+ const mockAxios = new MockAdapter(authManager.getAxiosInstance());
+ mockAxios.onGet().reply(HttpStatusCode.Ok, []);
+ render();
+ await waitFor(() => {
+ expect(screen.getByText("statistics.empty")).toBeVisible();
+ });
+ });
+
describe("the petition fails", () => {
each([HttpStatusCode.BadRequest, HttpStatusCode.NotFound,