diff --git a/webapp/public/locales/en/translation.json b/webapp/public/locales/en/translation.json index 88a9a527..ac05339f 100644 --- a/webapp/public/locales/en/translation.json +++ b/webapp/public/locales/en/translation.json @@ -15,7 +15,10 @@ "rules": "Rules", "dashboard": "User's Dashboard", "goBack": "Go Back", - "finish": "Finish" + "finish": "Finish", + "english": "English", + "spanish": "Spanish", + "language": "Language:" }, "session": { "username": "Username", @@ -68,5 +71,15 @@ "personalWrong": "{{wrong, number}} wrong answers", "personalRate": "{{rate, number}} %" } + }, + "game": { + "round": "Round", + "next": "Next" + }, + "about": { + "title": "About", + "description1": "We are the WIQ_EN2B group and we are delighted that you like 🥝KIWIQ🥝. Enjoy!!!", + "table1": "Developers🍌🍌", + "table2": "Contact" } } \ No newline at end of file diff --git a/webapp/public/locales/es/translation.json b/webapp/public/locales/es/translation.json index 42b37ac6..41085ba4 100644 --- a/webapp/public/locales/es/translation.json +++ b/webapp/public/locales/es/translation.json @@ -15,7 +15,10 @@ "rules": "Reglas", "dashboard": "Panel de Usuario", "goBack": "Volver", - "finish": "Finalizar" + "finish": "Finalizar", + "english": "Inglés", + "spanish": "Español", + "language": "Idioma:" }, "session": { "username": "Nombre de usuario", @@ -67,5 +70,15 @@ "personalWrong": "{{wrong, number}} respuestas incorrectas", "personalRate": "{{rate, number}} %" } + }, + "game": { + "round": "Ronda", + "next": "Siguiente" + }, + "about": { + "title": "Sobre nosotros", + "description1": "Somos el grupo WIQ_EN2B y estamos encantados de que te guste 🥝KIWIQ🥝. Disfruta!!!", + "table1": "Desarrolladores🍌🍌", + "table2": "Contacto" } } \ No newline at end of file diff --git a/webapp/src/components/LateralMenu.jsx b/webapp/src/components/LateralMenu.jsx new file mode 100644 index 00000000..dcce5e03 --- /dev/null +++ b/webapp/src/components/LateralMenu.jsx @@ -0,0 +1,123 @@ +import React, { useState, useEffect } from 'react'; +import PropTypes from 'prop-types'; +import { useNavigate } from "react-router-dom"; +import { useTranslation } from "react-i18next"; +import { Box, Drawer, DrawerOverlay, DrawerContent, DrawerCloseButton, DrawerHeader, DrawerBody, DrawerFooter, Select, Button, Text, IconButton, Flex, Image } from '@chakra-ui/react'; +import { FaChartBar, FaBook, FaTachometerAlt } from 'react-icons/fa'; +import { InfoIcon, SettingsIcon } from '@chakra-ui/icons'; + +import AuthManager from "components/auth/AuthManager"; + +const LateralMenu = ({ isOpen, onClose, changeLanguage, currentLanguage, isDashboard }) => { + const navigate = useNavigate(); + const [selectedLanguage, setSelectedLanguage] = useState(currentLanguage); + const [isLoggedIn, setIsLoggedIn] = useState(false); + const { t } = useTranslation(); + + useEffect(() => { + setSelectedLanguage(currentLanguage); + checkIsLoggedIn(); + }, [currentLanguage]); + + const handleChangeLanguage = (e) => { + const selectedLanguage = e.target.value; + changeLanguage(selectedLanguage); + }; + + const handleApiClick = () => { + window.open("http://localhost:8080/swagger/swagger-ui/index.html#/auth-controller/registerUser", "_blank", "noopener"); + }; + + const handleLogout = async () => { + try { + await new AuthManager().logout(); + navigate("/"); + } catch (error) { + console.error("Error al cerrar sesión:", error); + } + }; + + const checkIsLoggedIn = async () => { + try { + const loggedIn = await new AuthManager().isLoggedIn(); + setIsLoggedIn(loggedIn); + } catch (error) { + console.error("Error al verificar el estado de inicio de sesión:", error); + } + }; + + return ( + + + + + + App icon + KIWIQ + + + + + {t("common.language")} + + + + {isLoggedIn && ( + <> + {!isDashboard && ( + + + + )} + + + + + + + + + + + )} + + + + + + {isLoggedIn && ( + + )} + } className={"custom-button effect1"} onClick={() => {navigate("/about");}} margin={"10px"}> + + + + + + ); +}; + +LateralMenu.propTypes = { + isOpen: PropTypes.bool.isRequired, + onClose: PropTypes.func.isRequired, + changeLanguage: PropTypes.func.isRequired, + currentLanguage: PropTypes.string.isRequired, + isDashboard: PropTypes.bool.isRequired +}; + +export default LateralMenu; diff --git a/webapp/src/components/MenuButton.jsx b/webapp/src/components/MenuButton.jsx new file mode 100644 index 00000000..73e5a3e7 --- /dev/null +++ b/webapp/src/components/MenuButton.jsx @@ -0,0 +1,44 @@ +import React, { useState, useEffect } from "react"; +import PropTypes from 'prop-types'; +import { FaBars } from "react-icons/fa"; +import { chakra, Box, IconButton } from "@chakra-ui/react"; + +const MenuButton = ({ onClick }) => { + const [isFixed, setIsFixed] = useState(false); + + useEffect(() => { + const handleScroll = () => { + if (window.scrollY > 0) { + setIsFixed(true); + } else { + setIsFixed(false); + } + }; + + window.addEventListener("scroll", handleScroll); + return () => { + window.removeEventListener("scroll", handleScroll); + }; + }, []); + + return ( + + } + onClick={onClick} + /> + + ); +} + +MenuButton.propTypes = { + onClick: PropTypes.func.isRequired +}; + +export default MenuButton; \ No newline at end of file diff --git a/webapp/src/components/Router.jsx b/webapp/src/components/Router.jsx index 8769eb25..435928f7 100644 --- a/webapp/src/components/Router.jsx +++ b/webapp/src/components/Router.jsx @@ -11,6 +11,7 @@ import Results from "../pages/Results"; import Statistics from "pages/Statistics"; import ProtectedRoute from "./utils/ProtectedRoute"; import Logout from "pages/Logout"; +import About from "pages/About"; export default createRoutesFromElements( @@ -18,6 +19,7 @@ export default createRoutesFromElements( } /> } /> }/> + } /> }> }/> }/> diff --git a/webapp/src/i18n.js b/webapp/src/i18n.js index a9a6fdb1..84bc133d 100644 --- a/webapp/src/i18n.js +++ b/webapp/src/i18n.js @@ -8,6 +8,9 @@ export default i18n.use(Backend) .use(LanguageDetector) .use(initReactI18next) .init({ + backend: { + loadPath: '/locales/{{lng}}/translation.json' + }, fallbackLng: "en", debug: false }) \ No newline at end of file diff --git a/webapp/src/pages/About.jsx b/webapp/src/pages/About.jsx new file mode 100644 index 00000000..af99bb39 --- /dev/null +++ b/webapp/src/pages/About.jsx @@ -0,0 +1,74 @@ +import React, { useState } from "react"; +import { useTranslation } from 'react-i18next'; +import { Center, Heading, Stack, Box, Text, Table, Thead, Tr, Td, Th, Tbody, Container } from '@chakra-ui/react'; +import { InfoIcon } from '@chakra-ui/icons'; + +import LateralMenu from '../components/LateralMenu'; +import MenuButton from '../components/MenuButton'; +import GoBack from "components/GoBack"; + +export default function About() { + const { t, i18n } = useTranslation(); + const currentLanguage = i18n.language; + const [isMenuOpen, setIsMenuOpen] = useState(false); + + const changeLanguage = (selectedLanguage) => { + i18n.changeLanguage(selectedLanguage); + }; + + return ( +
+ setIsMenuOpen(true)} /> + setIsMenuOpen(false)} changeLanguage={changeLanguage} currentLanguage={currentLanguage} isDashboard={false}/> + + + + {t('about.title')} + + + {t("about.description1")} +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
{t("about.table1")}{t("about.table2")}
Gonzalo Alonso FernándezUO282104
Sergio Rodríguez GarcíaUO282598
Jorge Joaquín Gancedo FernándezUO282161
Darío Gutiérrez MoriUO282435
Sergio Quintana FernándezUO288090
Diego Villanueva BerrosUO283615
Gonzalo Suárez LosadaUO283928
+ +
+
+
+ ); +} \ No newline at end of file diff --git a/webapp/src/pages/Dashboard.jsx b/webapp/src/pages/Dashboard.jsx index 8e346ffe..a7ba0ee4 100644 --- a/webapp/src/pages/Dashboard.jsx +++ b/webapp/src/pages/Dashboard.jsx @@ -1,14 +1,17 @@ -import React from "react"; -import { Grid, Flex, Heading, Button, Box } from "@chakra-ui/react"; +import React, { useState } from "react"; +import { Heading, Button, Box, Stack } from "@chakra-ui/react"; import { Center } from "@chakra-ui/layout"; import { useNavigate } from "react-router-dom"; import { useTranslation } from "react-i18next"; -import ButtonEf from '../components/ButtonEf'; +import { FaTachometerAlt } from 'react-icons/fa'; + import AuthManager from "components/auth/AuthManager"; +import LateralMenu from '../components/LateralMenu'; +import MenuButton from '../components/MenuButton'; export default function Dashboard() { const navigate = useNavigate(); - const { t } = useTranslation(); + const { t, i18n } = useTranslation(); const handleLogout = async () => { try { @@ -19,22 +22,24 @@ export default function Dashboard() { } }; + const [isMenuOpen, setIsMenuOpen] = useState(false); + const currentLanguage = i18n.language; + const changeLanguage = (selectedLanguage) => { + i18n.changeLanguage(selectedLanguage); + }; + return (
+ setIsMenuOpen(true)} /> + setIsMenuOpen(false)} changeLanguage={changeLanguage} currentLanguage={currentLanguage} isDashboard={true}/> + {t("common.dashboard")} - - - navigate("/dashboard/rules")}/> - navigate("/dashboard/game")}/> - navigate("/dashboard/statistics")}/> - - - - - + + + + +
); diff --git a/webapp/src/pages/Game.jsx b/webapp/src/pages/Game.jsx index 957c0e55..19f4afb7 100644 --- a/webapp/src/pages/Game.jsx +++ b/webapp/src/pages/Game.jsx @@ -2,17 +2,21 @@ import React, { useState, useEffect, useCallback } from "react"; import { Grid, Flex, Heading, Button, Box, Text, Spinner } from "@chakra-ui/react"; import { Center } from "@chakra-ui/layout"; import { useNavigate } from "react-router-dom"; +import { useTranslation } from "react-i18next"; import Confetti from "react-confetti"; +import axios from "axios"; + import ButtonEf from '../components/ButtonEf'; import {getQuestion, answerQuestion} from '../components/game/Questions'; -import axios from "axios"; +import LateralMenu from '../components/LateralMenu'; +import MenuButton from '../components/MenuButton'; export default function Game() { const navigate = useNavigate(); const [question, setQuestion] = useState(null); const [loading, setLoading] = useState(true); - + const generateQuestion = useCallback(async () => { const result = await getQuestion(); if (result !== undefined) { @@ -73,9 +77,20 @@ export default function Game() { return () => clearTimeout(timeout); }, [showConfetti]); + const { t, i18n } = useTranslation(); + const [isMenuOpen, setIsMenuOpen] = useState(false); + + const currentLanguage = i18n.language; + const changeLanguage = (selectedLanguage) => { + i18n.changeLanguage(selectedLanguage); + }; + return (
- {`Round ${roundNumber}`} + setIsMenuOpen(true)} /> + setIsMenuOpen(false)} changeLanguage={changeLanguage} currentLanguage={currentLanguage} isDashboard={false}/> + + {t("game.round") + `${roundNumber}`} {`Correct answers: ${correctAnswers}`} @@ -98,8 +113,8 @@ export default function Game() { - diff --git a/webapp/src/pages/Login.jsx b/webapp/src/pages/Login.jsx index c3d82b42..61f80bb1 100644 --- a/webapp/src/pages/Login.jsx +++ b/webapp/src/pages/Login.jsx @@ -3,14 +3,15 @@ import { useTranslation } from "react-i18next"; import { useNavigate } from "react-router-dom"; import { FaLock, FaAddressCard } from "react-icons/fa"; import { Center } from "@chakra-ui/layout"; -import { Heading, Input, InputGroup, Stack, InputLeftElement, chakra, Box, Avatar, FormControl, InputRightElement, IconButton} from "@chakra-ui/react"; +import { Heading, Input, InputGroup, Stack, InputLeftElement, chakra, Box, Avatar, FormControl, InputRightElement, IconButton, Flex, Button} from "@chakra-ui/react"; import { ViewIcon, ViewOffIcon } from '@chakra-ui/icons'; -import ButtonEf from '../components/ButtonEf'; -import ErrorMessageAlert from "../components/ErrorMessageAlert"; + +import ErrorMessageAlert from "components/ErrorMessageAlert"; import AuthManager from "components/auth/AuthManager"; +import LateralMenu from 'components/LateralMenu'; +import MenuButton from 'components/MenuButton'; export default function Login() { - const navigate = useNavigate(); const navigateToDashboard = async () => { if (await new AuthManager().isLoggedIn()) { @@ -19,7 +20,7 @@ export default function Login() { } const [errorMessage, setErrorMessage] = useState(null); - const { t } = useTranslation(); + const { t, i18n } = useTranslation(); const [showPassword, setShowPassword] = useState(false); const changeShowP = () => setShowPassword(!showPassword); @@ -60,9 +61,20 @@ export default function Login() { navigateToDashboard(); + const [isMenuOpen, setIsMenuOpen] = useState(false); + + const currentLanguage = i18n.language; + const changeLanguage = (selectedLanguage) => { + i18n.changeLanguage(selectedLanguage); + }; + return (
+ + setIsMenuOpen(true)} /> + setIsMenuOpen(false)} changeLanguage={changeLanguage} currentLanguage={currentLanguage} isDashboard={false}/> + {t("common.login")} @@ -100,7 +112,10 @@ export default function Login() { - + + + + diff --git a/webapp/src/pages/Root.jsx b/webapp/src/pages/Root.jsx index 5de99b7d..349551e9 100644 --- a/webapp/src/pages/Root.jsx +++ b/webapp/src/pages/Root.jsx @@ -1,15 +1,19 @@ -import React from "react"; +import React, { useState } from "react"; import { useTranslation } from "react-i18next"; import { useNavigate } from "react-router-dom"; import { Center } from "@chakra-ui/layout"; import { Text, Heading, Stack, Link, Image, Box } from "@chakra-ui/react"; +import MenuButton from '../components/MenuButton'; +import LateralMenu from '../components/LateralMenu'; import ButtonEf from '../components/ButtonEf'; import AuthManager from "components/auth/AuthManager"; export default function Root() { const navigate = useNavigate(); - const { t } = useTranslation(); + const { t, i18n } = useTranslation(); + const [isMenuOpen, setIsMenuOpen] = useState(false); + const currentLanguage = i18n.language; const navigateToDashboard = async () => { if (await new AuthManager().isLoggedIn()) { @@ -18,19 +22,28 @@ export default function Root() { } navigateToDashboard(); + const changeLanguage = (selectedLanguage) => { + i18n.changeLanguage(selectedLanguage); + }; + return (
- - kiwiq icon - - - {"KIWIQ"} - {t("session.welcome")} - navigate("/login")}/> - {t("session.account")} navigate("/signup")}>{t("session.clickHere")} - - - + setIsMenuOpen(true)} /> + setIsMenuOpen(false)} changeLanguage={changeLanguage} currentLanguage={currentLanguage} isDashboard={false}/> + +
+ + kiwiq icon + + + {"KIWIQ"} + {t("session.welcome")} + navigate("/login")}/> + {t("session.account")} navigate("/signup")}>{t("session.clickHere")} + + + +
); } \ No newline at end of file diff --git a/webapp/src/pages/Rules.jsx b/webapp/src/pages/Rules.jsx index 01302b31..9e3c2855 100644 --- a/webapp/src/pages/Rules.jsx +++ b/webapp/src/pages/Rules.jsx @@ -1,14 +1,28 @@ -import React from "react"; +import React, { useState } from "react"; import { useTranslation } from "react-i18next"; import { Center } from "@chakra-ui/layout"; import { Text, Heading, Box } from "@chakra-ui/react"; +import { FaBook } from 'react-icons/fa'; + import GoBack from "components/GoBack"; +import LateralMenu from '../components/LateralMenu'; +import MenuButton from '../components/MenuButton'; export default function Rules() { - const { t } = useTranslation(); + const { t, i18n } = useTranslation(); + + const [isMenuOpen, setIsMenuOpen] = useState(false); + + const currentLanguage = i18n.language; + const changeLanguage = (selectedLanguage) => { + i18n.changeLanguage(selectedLanguage); + }; return (
+ setIsMenuOpen(true)} /> + setIsMenuOpen(false)} changeLanguage={changeLanguage} currentLanguage={currentLanguage} isDashboard={false}/> + {t("common.rules")} diff --git a/webapp/src/pages/Signup.jsx b/webapp/src/pages/Signup.jsx index 4a4a0890..6ebf5c1d 100644 --- a/webapp/src/pages/Signup.jsx +++ b/webapp/src/pages/Signup.jsx @@ -1,13 +1,15 @@ import { Center } from "@chakra-ui/layout"; -import { Heading, Input, InputGroup, Stack, InputLeftElement, chakra, Box, Avatar, FormControl, InputRightElement, FormHelperText, IconButton} from "@chakra-ui/react"; +import { Heading, Input, InputGroup, Stack, InputLeftElement, chakra, Box, Avatar, FormControl, InputRightElement, FormHelperText, IconButton, Flex, Button} from "@chakra-ui/react"; import { ViewIcon, ViewOffIcon } from '@chakra-ui/icons' import React, { useState } from "react"; import { useTranslation } from "react-i18next"; import { useNavigate } from "react-router-dom"; import { FaUserAlt, FaLock, FaAddressCard } from "react-icons/fa"; -import ButtonEf from '../components/ButtonEf'; -import ErrorMessageAlert from "../components/ErrorMessageAlert"; + +import ErrorMessageAlert from "components/ErrorMessageAlert"; import AuthManager from "components/auth/AuthManager"; +import LateralMenu from 'components/LateralMenu'; +import MenuButton from 'components/MenuButton'; export default function Signup() { const [email, setEmail] = useState(""); @@ -19,7 +21,7 @@ export default function Signup() { const [errorMessage, setErrorMessage] = useState(null); const navigate = useNavigate(); - const { t } = useTranslation(); + const { t, i18n } = useTranslation(); const ChakraFaCardAlt = chakra(FaAddressCard); const ChakraFaUserAlt = chakra(FaUserAlt); @@ -86,8 +88,18 @@ export default function Signup() { navigateToDashboard(); + const [isMenuOpen, setIsMenuOpen] = useState(false); + + const currentLanguage = i18n.language; + const changeLanguage = (selectedLanguage) => { + i18n.changeLanguage(selectedLanguage); + }; + return (
+ setIsMenuOpen(true)} /> + setIsMenuOpen(false)} changeLanguage={changeLanguage} currentLanguage={currentLanguage} isDashboard={false}/> + @@ -160,7 +172,10 @@ export default function Signup() { Las contraseñas no coinciden )} - + + + + diff --git a/webapp/src/pages/Statistics.jsx b/webapp/src/pages/Statistics.jsx index 1bb1899a..a386e23c 100644 --- a/webapp/src/pages/Statistics.jsx +++ b/webapp/src/pages/Statistics.jsx @@ -7,9 +7,13 @@ import { useTranslation } from "react-i18next"; import GoBack from "components/GoBack"; import AuthManager from "components/auth/AuthManager"; import { HttpStatusCode } from "axios"; +import { FaChartBar } from 'react-icons/fa'; + +import LateralMenu from '../components/LateralMenu'; +import MenuButton from '../components/MenuButton'; const UserVisual = (props) => { - const {t} = useTranslation(); + const { t } = useTranslation(); const topTen = props.topTen; const userData = props.userData; const [tooSmall] = useMediaQuery("(max-width: 800px)"); @@ -107,7 +111,7 @@ const UserVisual = (props) => { } export default function Statistics() { - const {t} = useTranslation(); + const { t, i18n } = useTranslation(); const [retrievedData, setRetrievedData] = useState(false); const [topTen, setTopTen] = useState([]); const [userData, setUserData] = useState({ @@ -146,9 +150,20 @@ export default function Statistics() { } } + const [isMenuOpen, setIsMenuOpen] = useState(false); + + const currentLanguage = i18n.language; + const changeLanguage = (selectedLanguage) => { + i18n.changeLanguage(selectedLanguage); + }; + + return (
+ setIsMenuOpen(true)} /> + setIsMenuOpen(false)} changeLanguage={changeLanguage} currentLanguage={currentLanguage} isDashboard={false}/> + {t("common.statistics.title")} } minW="30vw" minH="50vh" p="1rem" backgroundColor="whiteAlpha.900" shadow="2xl" diff --git a/webapp/src/tests/About.test.js b/webapp/src/tests/About.test.js new file mode 100644 index 00000000..86cc766e --- /dev/null +++ b/webapp/src/tests/About.test.js @@ -0,0 +1,32 @@ +import React from 'react'; +import { render, fireEvent, waitFor } from '@testing-library/react'; +import '@testing-library/jest-dom/extend-expect'; +import { MemoryRouter } from 'react-router-dom'; +import About from '../pages/About'; +import theme from '../styles/theme'; +import { ChakraProvider } from '@chakra-ui/react'; + +jest.mock('react-i18next', () => ({ + useTranslation: () => ({ + t: key => key, + i18n: { + changeLanguage: () => new Promise(() => {}), + }, + }), +})); + +describe('About Component', () => { + it('renders title and description correctly', () => { + const { getByText } = render(); + + expect(getByText('about.title')).toBeInTheDocument(); + expect(getByText('about.description1')).toBeInTheDocument(); + }); + + it('renders table rows with correct data', () => { + const { getByText } = render(); + + expect(getByText('Gonzalo Alonso Fernández')).toBeInTheDocument(); + expect(getByText('UO282104')).toBeInTheDocument(); + }); +}); diff --git a/webapp/src/tests/Dashboard.test.js b/webapp/src/tests/Dashboard.test.js index bf56f45e..0116c55c 100644 --- a/webapp/src/tests/Dashboard.test.js +++ b/webapp/src/tests/Dashboard.test.js @@ -34,31 +34,11 @@ describe('Dashboard component', () => { expect(getByText("common.dashboard")).toBeInTheDocument(); - expect(screen.getByTestId('Rules')).toBeInTheDocument(); expect(screen.getByTestId('Play')).toBeInTheDocument(); - expect(screen.getByTestId('Statistics')).toBeInTheDocument(); expect(screen.getByText(/logout/i)).toBeInTheDocument(); }); - it('navigates to the rules route on button click', () => { - render(); - - const rulesButton = screen.getByTestId('Rules'); - fireEvent.click(rulesButton); - - expect(screen.getByText("common.rules")).toBeInTheDocument(); - }); - - it('do not navigates to the statistics route on button click', () => { - render(); - - const statisticsButton = screen.getByTestId('Statistics'); - fireEvent.click(statisticsButton); - - expect(screen.getByText("common.dashboard")).toBeInTheDocument(); - }); - it('navigates to the game route on "Play" button click', () => { render(); @@ -68,15 +48,6 @@ describe('Dashboard component', () => { expect(screen.getByText("common.play")).toBeInTheDocument(); }); - it('does not navigate to the statistics route on button click', () => { - render(); - - const statisticsButton = screen.getByTestId('Statistics'); - fireEvent.click(statisticsButton); - - expect(screen.getByText("common.dashboard")).toBeInTheDocument(); - }); - it('handles logout successfully', async () => { render(); mockAxios.onGet().replyOnce(HttpStatusCode.Ok); @@ -89,13 +60,4 @@ describe('Dashboard component', () => { expect(mockAxios.history.get.length).toBe(1); expect(screen.getByText("common.dashboard")).toBeInTheDocument(); }); - - it('does not navigate to the statistics route on disabled button click', () => { - render(); - - const statisticsButton = screen.getByTestId('Statistics'); - fireEvent.click(statisticsButton); - - expect(screen.getByText("common.dashboard")).toBeInTheDocument(); - }); }); diff --git a/webapp/src/tests/Game.test.js b/webapp/src/tests/Game.test.js index fada5791..07d4a293 100644 --- a/webapp/src/tests/Game.test.js +++ b/webapp/src/tests/Game.test.js @@ -47,7 +47,7 @@ describe('Game component', () => { test('disables next button when no option is selected', async () => { render(); - const nextButton = await screen.findByText('Next'); + const nextButton = await screen.findByTestId('Next'); expect(nextButton).toBeDisabled(); }); @@ -55,7 +55,7 @@ describe('Game component', () => { test('enables next button when an option is selected', async () => { render(); const option1Button = await screen.findByTestId('Option1'); - const nextButton = await screen.findByText('Next'); + const nextButton = await screen.findByTestId('Next'); act(() => fireEvent.click(option1Button)); diff --git a/webapp/src/tests/LateralMenu.test.js b/webapp/src/tests/LateralMenu.test.js new file mode 100644 index 00000000..24f6ef78 --- /dev/null +++ b/webapp/src/tests/LateralMenu.test.js @@ -0,0 +1,132 @@ +import React from 'react'; +import { render, screen, fireEvent, waitFor } 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'; + +jest.mock('react-i18next', () => ({ + useTranslation: () => { + return { + t: (str) => str, + i18n: { + changeLanguage: () => new Promise(() => {}), + }, + } + }, +})); + +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useNavigate: jest.fn(), +})); + + const authManager = new AuthManager(); + +describe('LateralMenu component', () => { + beforeEach(() => { + authManager.reset(); + jest.clearAllMocks(); + }); + + const props = { + isOpen: true, + onClose: jest.fn(), + changeLanguage: jest.fn(), + currentLanguage: 'es', + isLoggedIn: true, + isDashboard: false, + }; + + it('renders KIWIQ heading', () => { + render(); + const headingElement = screen.getByText('KIWIQ'); + expect(headingElement).toBeInTheDocument(); + }); + + it('renders language select', () => { + render(); + const languageSelect = screen.getByText('common.language'); + expect(languageSelect).toBeInTheDocument(); + }); + + it('changes language when select value is changed', () => { + render(); + const selectElement = screen.getByTestId('language-select'); + fireEvent.change(selectElement, { target: { value: 'en' } }); + expect(props.changeLanguage).toHaveBeenCalledWith('en'); + }); + + it('does not render dashboard button when isLoggedIn is false', () => { + const newProps = { ...props, isLoggedIn: false }; + render(); + const dashboardButton = screen.queryByText('common.dashboard'); + expect(dashboardButton).toBeNull(); + }); + + it('does not render dashboard button when isLoggedIn is false', () => { + const newProps = { ...props, isLoggedIn: false }; + render(); + const dashboardButton = screen.queryByText('common.dashboard'); + expect(dashboardButton).toBeNull(); + }); + + it('renders API button when isLoggedIn is true', async () => { + authManager.setLoggedIn(true); + const { getByText } = render(); + await waitFor(() => { + expect(getByText('API')).toBeInTheDocument(); + }); + }); + + it('does not render API button when isLoggedIn is false', () => { + const newProps = { ...props, isLoggedIn: false }; + render(); + const apiButton = screen.queryByText('API'); + expect(apiButton).toBeNull(); + }); + + it('renders statistics button when isLoggedIn is true', async () => { + authManager.setLoggedIn(true); + const { getByText } = render(); + await waitFor(() => { + expect(getByText('common.statistics.title')).toBeInTheDocument(); + }); + }); + + it('does not render statistics button when isLoggedIn is false', () => { + const newProps = { ...props, isLoggedIn: false }; + render(); + const statisticsButton = screen.queryByText('common.statistics.title'); + expect(statisticsButton).toBeNull(); + }); + + it('renders rules button when isLoggedIn is true', async () => { + authManager.setLoggedIn(true); + const { getByText } = render(); + await waitFor(() => { + expect(getByText('common.rules')).toBeInTheDocument(); + }); + }); + + it('does not render rules button when isLoggedIn is false', () => { + const newProps = { ...props, isLoggedIn: false }; + render(); + const rulesButton = screen.queryByText('common.rules'); + expect(rulesButton).toBeNull(); + }); + + it('does not render logout button when isLoggedIn is false', () => { + const newProps = { ...props, isLoggedIn: false }; + render(); + const logoutButton = screen.queryByText('common.logout'); + expect(logoutButton).toBeNull(); + }); + + it('renders about button', () => { + render(); + const aboutButton = screen.getByLabelText('About'); + expect(aboutButton).toBeInTheDocument(); + }); +}); diff --git a/webapp/src/tests/Signup.test.js b/webapp/src/tests/Signup.test.js index 11cdde4e..edb28544 100644 --- a/webapp/src/tests/Signup.test.js +++ b/webapp/src/tests/Signup.test.js @@ -1,5 +1,5 @@ import React from 'react'; -import { render, fireEvent, getByTestId, getAllByTestId, waitFor, act } from '@testing-library/react'; +import { render, fireEvent, getByTestId, getAllByTestId } from '@testing-library/react'; import { MemoryRouter } from 'react-router'; import Signup from '../pages/Signup'; import { ChakraProvider } from '@chakra-ui/react';