From b9efe753a46afd467fb75ce083cb631d36249b1e Mon Sep 17 00:00:00 2001 From: uo289267 Date: Sun, 14 Apr 2024 18:13:06 +0200 Subject: [PATCH 01/40] Added favicon and title to webapp --- webapp/public/favicon.ico | Bin 3870 -> 15406 bytes webapp/src/App.js | 6 +++++- webapp/src/index.js | 4 ++++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/webapp/public/favicon.ico b/webapp/public/favicon.ico index a11777cc471a4344702741ab1c8a588998b1311a..5019a7d6d97bc325ca1e23a4f78d9172773a533c 100644 GIT binary patch literal 15406 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 literal 3870 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 baj7?x%VD!5Cxk2YbYLNVeiXvvpMCWYo=by@ diff --git a/webapp/src/App.js b/webapp/src/App.js index 5eefcf70..46edf703 100644 --- a/webapp/src/App.js +++ b/webapp/src/App.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React,{ useEffect } from 'react'; import QuestionView from './components/questionView/QuestionView'; import GameMenu from './components/GameMenu/GameMenu'; import Navbar from './components/fragments/NavBar'; @@ -12,7 +12,11 @@ import './custom.css'; import HistoricalView from './components/HistoricalData/HistoricalView'; import { UserContextProvider } from './components/loginAndRegistration/UserContext'; + function App() { + useEffect(() => { + document.title = 'WIQ'; + }, []); return ( diff --git a/webapp/src/index.js b/webapp/src/index.js index 409a8681..05cbd873 100644 --- a/webapp/src/index.js +++ b/webapp/src/index.js @@ -33,6 +33,10 @@ i18next.init({ const root = ReactDOM.createRoot(document.getElementById('root')); root.render( + + + + {/* envolviendo la app con el sistema de traducciones */} From 690c90a3681bb1031ff9e92b0c5c782f808cc817 Mon Sep 17 00:00:00 2001 From: uo289267 Date: Sun, 14 Apr 2024 20:27:31 +0200 Subject: [PATCH 02/40] Added one back button --- webapp/src/components/GameMenu/GameMenu.js | 6 ++++-- .../src/components/HistoricalData/HistoricalView.js | 11 ++++++++++- webapp/src/translations/en/global.json | 3 ++- webapp/src/translations/es/global.json | 3 ++- 4 files changed, 18 insertions(+), 5 deletions(-) diff --git a/webapp/src/components/GameMenu/GameMenu.js b/webapp/src/components/GameMenu/GameMenu.js index 7f7bd957..04c08f45 100644 --- a/webapp/src/components/GameMenu/GameMenu.js +++ b/webapp/src/components/GameMenu/GameMenu.js @@ -9,8 +9,8 @@ export default function GameMenu() { return (

{t("gameMenu.title")}

- +
); } @@ -22,4 +22,6 @@ export default function GameMenu() {

{t("gameMenu.new_game_button")}

); - } \ No newline at end of file + } + + \ No newline at end of file diff --git a/webapp/src/components/HistoricalData/HistoricalView.js b/webapp/src/components/HistoricalData/HistoricalView.js index f110430c..c9415240 100644 --- a/webapp/src/components/HistoricalData/HistoricalView.js +++ b/webapp/src/components/HistoricalData/HistoricalView.js @@ -2,7 +2,7 @@ import React, { useState } from 'react'; import {useTranslation} from "react-i18next"; import HistoryRecordRetriever from './HistoryRecordRetriever'; import { useUserContext } from '../loginAndRegistration/UserContext'; - +import { Link } from "react-router-dom"; import RecordList from './RecordList'; @@ -29,6 +29,7 @@ export default function HistoricalView() { return (
+ {(records && records.length !== 0) ? records.map((record, index) => ( )):

{t("historicalView.no_games_played")}

} @@ -53,4 +54,12 @@ function HistoricalGameElement({record,t}){
); +} + +function BackButton({t}){ + return( + +

⬅️{t("gameMenu.back")}

+ + ); } \ No newline at end of file diff --git a/webapp/src/translations/en/global.json b/webapp/src/translations/en/global.json index 8f79adda..70c3da87 100644 --- a/webapp/src/translations/en/global.json +++ b/webapp/src/translations/en/global.json @@ -55,7 +55,8 @@ "gameMenu":{ "history_button":"View Historical Data", "new_game_button":"Create New Game", - "title":"Game Menu" + "title":"Game Menu", + "back":"Back" }, "questionView":{ "seconds":"seconds", diff --git a/webapp/src/translations/es/global.json b/webapp/src/translations/es/global.json index 410eebfa..8c878915 100644 --- a/webapp/src/translations/es/global.json +++ b/webapp/src/translations/es/global.json @@ -59,7 +59,8 @@ "gameMenu":{ "history_button":"Ver Historial", "new_game_button":"Crear nuevo juego", - "title":"Menú del Juego" + "title":"Menú del Juego", + "back":"Atrás" },"questionView":{ "seconds":"segundos", "question_counter":"Pregunta nº ", From 1710458f9be1cf46b4e435dbd6f28feff8e62dc5 Mon Sep 17 00:00:00 2001 From: uo289267 Date: Sun, 14 Apr 2024 21:06:05 +0200 Subject: [PATCH 03/40] Added back Buttons to GameMenu and Mocks --- .../HistoricalData/HistoricalView.js | 13 +--- .../HistoricalData/HistoryRecordRetriever.js | 78 ++++++++++++++++--- .../fragments/BackButtonToGameMenu.js | 9 +++ .../questionView/QuestionGenerator.js | 27 ++++++- .../components/questionView/QuestionView.js | 2 + webapp/src/custom.css | 19 +++++ 6 files changed, 126 insertions(+), 22 deletions(-) create mode 100644 webapp/src/components/fragments/BackButtonToGameMenu.js diff --git a/webapp/src/components/HistoricalData/HistoricalView.js b/webapp/src/components/HistoricalData/HistoricalView.js index c9415240..9812acc7 100644 --- a/webapp/src/components/HistoricalData/HistoricalView.js +++ b/webapp/src/components/HistoricalData/HistoricalView.js @@ -2,7 +2,7 @@ import React, { useState } from 'react'; import {useTranslation} from "react-i18next"; import HistoryRecordRetriever from './HistoryRecordRetriever'; import { useUserContext } from '../loginAndRegistration/UserContext'; -import { Link } from "react-router-dom"; +import BackButtonToGameMenu from '../fragments/BackButtonToGameMenu'; import RecordList from './RecordList'; @@ -16,7 +16,7 @@ export default function HistoricalView() { const getRecords = async ()=>{ try { - var jsonRecords = await retriever.getRecords(user.username); + var jsonRecords = await retriever.getRecords("user"); var recordsArray = jsonRecords.games; setRecords(recordsArray); } catch (error) { @@ -29,7 +29,7 @@ export default function HistoricalView() { return (
- + {(records && records.length !== 0) ? records.map((record, index) => ( )):

{t("historicalView.no_games_played")}

} @@ -56,10 +56,3 @@ function HistoricalGameElement({record,t}){ ); } -function BackButton({t}){ - return( - -

⬅️{t("gameMenu.back")}

- - ); -} \ No newline at end of file diff --git a/webapp/src/components/HistoricalData/HistoryRecordRetriever.js b/webapp/src/components/HistoricalData/HistoryRecordRetriever.js index d586a867..c240853e 100644 --- a/webapp/src/components/HistoricalData/HistoryRecordRetriever.js +++ b/webapp/src/components/HistoricalData/HistoryRecordRetriever.js @@ -8,18 +8,74 @@ class HistoryRecordRetriever{ } async getRecords(user) { - try { - const response = await axios.get(this.apiUrl + '/' + user); - const receivedRecords = await response.data; - console.log(receivedRecords) - console.log(receivedRecords[0]) - return receivedRecords.record; - } catch (error) { - console.log(error) - throw new Error(error); - - } + return { + userId: user, + games: [ + { + questions: [ + { + question: "¿Cuál es la capital de Francia?", + answers: ["Madrid", "París", "Londres", "Roma"], + answerGiven: "París", + correctAnswer: "París" + }, + { + question: "¿En qué año comenzó la Segunda Guerra Mundial?", + answers: ["1939", "1945", "1914", "1941"], + answerGiven: "1939", + correctAnswer: "1939" + }, + { + question: "¿Quién escribió 'Don Quijote de la Mancha'?", + answers: ["Miguel de Cervantes", "Gabriel García Márquez", "Federico García Lorca", "Jorge Luis Borges"], + answerGiven: "Miguel de Cervantes", + correctAnswer: "Miguel de Cervantes" + } + ], + points: 3000, + date: "01/02/24" + }, + { + questions: [ + { + question: "¿Cuál es el río más largo del mundo?", + answers: ["Nilo", "Amazonas", "Yangtsé", "Misisipi"], + answerGiven: "Amazonas", + correctAnswer: "Amazonas" + }, + { + question: "¿Cuál es el elemento más abundante en la corteza terrestre?", + answers: ["Hierro", "Oxígeno", "Silicio", "Aluminio"], + answerGiven: "Oxígeno", + correctAnswer: "Oxígeno" + } + ], + points: 2500, + date: "02/02/24" + }, + { + questions: [ + { + question: "¿Quién pintó la Mona Lisa?", + answers: ["Leonardo da Vinci", "Pablo Picasso", "Vincent van Gogh", "Rembrandt"], + answerGiven: "Leonardo da Vinci", + correctAnswer: "Leonardo da Vinci" + }, + { + question: "¿Cuál es el planeta más grande del sistema solar?", + answers: ["Júpiter", "Saturno", "Neptuno", "Urano"], + answerGiven: "Júpiter", + correctAnswer: "Júpiter" + } + ], + points: 3500, + date: "03/02/24" + } + ] + }; } + + } diff --git a/webapp/src/components/fragments/BackButtonToGameMenu.js b/webapp/src/components/fragments/BackButtonToGameMenu.js new file mode 100644 index 00000000..e9bab12c --- /dev/null +++ b/webapp/src/components/fragments/BackButtonToGameMenu.js @@ -0,0 +1,9 @@ +import { Link } from "react-router-dom"; + +export default function BackButton({t}){ + return( + +

⬅ {t("gameMenu.back")}

+ + ); + } \ No newline at end of file diff --git a/webapp/src/components/questionView/QuestionGenerator.js b/webapp/src/components/questionView/QuestionGenerator.js index 28038332..ff9058de 100644 --- a/webapp/src/components/questionView/QuestionGenerator.js +++ b/webapp/src/components/questionView/QuestionGenerator.js @@ -9,6 +9,31 @@ class QuestionGenerator{ } async generateQuestions(lang) { + try { + //const response = await fetch(this.apiUrl); + //const receivedQuestions = await response.json(); + + //Mockup + const receivedQuestions = JSON.parse('{"0":{"question":"¿Cuál es la población de Oviedo?","answers":["225089","191325","220587","121548"]},'+ + '"1":{"question":"¿Cuál es la población de Gijón?","answers":["275274","159658","233982","305554"]},'+ + '"2":{"question":"¿Cuál es la población de Avilés?","answers":["82568","115595","41284","122200"]},'+ + '"3":{"question":"¿Cuál es la capital de Asturias?","answers":["Ciudad de Oviedo","a","b","c"]},'+ + '"4":{"question":"¿Cuál es la capital de España?","answers":["Madrid","a","b","c"]},'+ + '"5":{"question":"¿Cuál es la capital de Turquía?","answers":["Ankara","a","b","c"]}}') + + let i = 0; + var questions = []; + for (const key in receivedQuestions) { + questions[i] = new Question(receivedQuestions[key]); + i += 1; + } + console.log(questions); + return questions; + } catch (error) { + throw new Error(error); + } + + /* try { const response = await axios.get(this.apiUrl + '/' + lang); const receivedQuestions = await response.data; @@ -21,7 +46,7 @@ class QuestionGenerator{ return questions; } catch (error) { throw new Error(error); - } + }*/ } } diff --git a/webapp/src/components/questionView/QuestionView.js b/webapp/src/components/questionView/QuestionView.js index 96d0978c..cf4ea79c 100644 --- a/webapp/src/components/questionView/QuestionView.js +++ b/webapp/src/components/questionView/QuestionView.js @@ -9,6 +9,7 @@ import $ from 'jquery'; import RecordList from '../HistoricalData/RecordList'; import ButtonHistoricalData from "../HistoricalData/ButtonHistoricalData"; import { useUserContext } from '../loginAndRegistration/UserContext'; +import BackButtonToGameMenu from '../fragments/BackButtonToGameMenu'; const creationHistoricalRecord = new CreationHistoricalRecord(); const questionGenerator = new QuestionGenerator(); @@ -146,6 +147,7 @@ function QuestionComponent({questions, numQuestion, handleClick, t, points}){ ) : ( <>

{t("questionView.finished_game")}

+

{points} {t("questionView.point")}

    < RecordList record={creationHistoricalRecord.getRecord().game}/>
diff --git a/webapp/src/custom.css b/webapp/src/custom.css index daa780fd..39d91582 100644 --- a/webapp/src/custom.css +++ b/webapp/src/custom.css @@ -1216,6 +1216,25 @@ svg { /*------------------------------Historical--------------------------------------------*/ /* Estilos para los botones */ +.linkButtonHistorical{ + display: flex; + justify-content: center; + align-items: center; + width: 10em; + height: 45px; + background:#00b8ff; + border: none; + outline: none; + border-radius: 40px; + box-shadow: 0 0 10px black; + cursor: pointer; + font-size: 1em; + color: black; + font-weight: 700; + margin: 1em; + text-decoration: none; + +} .historicalButton { color: white; width: 50em; From 4153efcd52b2a1e311ff54dc357d1f4f7334fab8 Mon Sep 17 00:00:00 2001 From: uo289267 Date: Sun, 14 Apr 2024 22:18:31 +0200 Subject: [PATCH 04/40] Added Game configuration View to configurate customized games and competitive games --- webapp/src/App.js | 2 + .../GameConfigurator/GameConfigurator.js | 57 +++++++++++++++++++ webapp/src/components/GameMenu/GameMenu.js | 2 +- webapp/src/translations/en/global.json | 12 ++++ webapp/src/translations/es/global.json | 13 ++++- 5 files changed, 84 insertions(+), 2 deletions(-) create mode 100644 webapp/src/components/GameConfigurator/GameConfigurator.js diff --git a/webapp/src/App.js b/webapp/src/App.js index 46edf703..9ed9bbde 100644 --- a/webapp/src/App.js +++ b/webapp/src/App.js @@ -11,6 +11,7 @@ import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-d import './custom.css'; import HistoricalView from './components/HistoricalData/HistoricalView'; import { UserContextProvider } from './components/loginAndRegistration/UserContext'; +import GameConfigurator from './components/GameConfigurator/GameConfigurator'; function App() { @@ -32,6 +33,7 @@ function App() { } /> } /> } /> + }/>
diff --git a/webapp/src/components/GameConfigurator/GameConfigurator.js b/webapp/src/components/GameConfigurator/GameConfigurator.js new file mode 100644 index 00000000..7413dcc2 --- /dev/null +++ b/webapp/src/components/GameConfigurator/GameConfigurator.js @@ -0,0 +1,57 @@ +import React, { useState } from 'react'; +import {useTranslation} from "react-i18next"; +import { Link } from "react-router-dom"; + +function GameConfigurator(){ + const [tipoPregunta, setTipoPregunta] = useState(''); + const [numeroPreguntas, setNumeroPreguntas] = useState(5); + const[t, i18n] = useTranslation("global"); + + return ( +
+

{t("gameConfigurator.game_config")}

+ + + +

+ + + {/* Spinner para seleccionar el número de preguntas */} + setNumeroPreguntas(e.target.value)} + min="1" + /> +

+ +

+

{t("gameConfigurator.rules_competi")}

+ {/* Botones para jugar un juego personalizado o competitivo */} + + + +
+ ); +} + +function ButtonCustomized({t, type, number}){ + return( + + ); +} + +function ButtonCompetitive({t}){ + //llamar setTipoPregunta COMPETITIVE + return( + + ); +} + + +export default GameConfigurator; diff --git a/webapp/src/components/GameMenu/GameMenu.js b/webapp/src/components/GameMenu/GameMenu.js index 04c08f45..e0e65053 100644 --- a/webapp/src/components/GameMenu/GameMenu.js +++ b/webapp/src/components/GameMenu/GameMenu.js @@ -18,7 +18,7 @@ export default function GameMenu() { function ButtonNewGame({ t }) { return ( - +

{t("gameMenu.new_game_button")}

); diff --git a/webapp/src/translations/en/global.json b/webapp/src/translations/en/global.json index 70c3da87..2a2963dd 100644 --- a/webapp/src/translations/en/global.json +++ b/webapp/src/translations/en/global.json @@ -70,6 +70,18 @@ "game":"Game", "points":"points", "no_games_played":"No games played yet" + }, + "gameConfigurator":{ + "game_config":"Game configuration", + "type_quest":"Type of question: ", + "num_quest":"Number of questions: ", + "play_custom":"Play Customized Game", + "rules_competi":"Play with all kinds of questions and a quantity of 5", + "play_competi":"Play competitive Game", + "option_population":"Population", + "option_capital":"Capital", + "option_language":"Language", + "option_size":"Size" } } diff --git a/webapp/src/translations/es/global.json b/webapp/src/translations/es/global.json index 8c878915..2f855737 100644 --- a/webapp/src/translations/es/global.json +++ b/webapp/src/translations/es/global.json @@ -73,8 +73,19 @@ "game":"Partida", "points":"puntos", "no_games_played":"No has jugado todavía" + }, + "gameConfigurator":{ + "game_config":"Configuración del Juego", + "type_quest":"Tipo de Pregunta", + "num_quest":"Numero de Preguntas", + "play_custom":"Jugar personalizado", + "rules_competi":"Jugar con todo tipo de preguntas siendo estas 5", + "play_competi":"Jugar Competitivo", + "option_population":"Población", + "option_capital":"Capital", + "option_language":"Lenguaje", + "option_size":"Extensión" } - } \ No newline at end of file From 6b48eacb91f96baf3c966d0201760d351941b898 Mon Sep 17 00:00:00 2001 From: Mister-Mario Date: Mon, 15 Apr 2024 00:03:39 +0200 Subject: [PATCH 05/40] Added new endpoints for taking different sizes of questions --- gatewayservice/gateway-service.js | 14 ++++++++++++ gatewayservice/gateway-service.test.js | 12 +++++++++++ questionservice/question-service.js | 27 ++++++++++++++++++++++++ questionservice/question-service.test.js | 18 +++++++++++++++- 4 files changed, 70 insertions(+), 1 deletion(-) diff --git a/gatewayservice/gateway-service.js b/gatewayservice/gateway-service.js index 009db707..12036c07 100644 --- a/gatewayservice/gateway-service.js +++ b/gatewayservice/gateway-service.js @@ -58,6 +58,20 @@ app.get('/questions', async (req, res) => { } }); +app.get('/questions/:lang/:amount', async (req, res) => { + try { + const lang = req.params.lang; + const amount = req.params.amount; + // Forward the question request to the quetion service + const questionResponse = await axios.get(questionServiceUrl+'/questions/' + lang + '/' + amount); + + res.json(questionResponse.data); + } catch (error) { + + res.status(error.response.status).json({ error: error.response.data.error }); + } +}); + app.get('/questions/:lang', async (req, res) => { try { const lang = req.params.lang; diff --git a/gatewayservice/gateway-service.test.js b/gatewayservice/gateway-service.test.js index bfed664f..69a771dd 100644 --- a/gatewayservice/gateway-service.test.js +++ b/gatewayservice/gateway-service.test.js @@ -24,6 +24,9 @@ describe('Gateway Service', () => { if (url.endsWith('/questions')){ return Promise.resolve({ data: [{question: "¿Cuál es la población de Oviedo?", answers: ["225089","272357","267855","231841"]}] }); + } else if (url.endsWith('/questions/es/1')){ + return Promise.resolve({ data: [{question: "¿Cuál es la población de Oviedo?", + answers: ["225089","272357","267855","231841"]}] }); } else if (url.endsWith('/questions/es')){ return Promise.resolve({ data: [{question: "¿Cuál es la población de Oviedo?", answers: ["225089","272357","267855","231841"]}] }); @@ -72,6 +75,15 @@ describe('Gateway Service', () => { expect(response.body[0]).toHaveProperty('question', "¿Cuál es la población de Oviedo?"); }); + // Test /questions/:lang/:amount endpoint + it('should forward questions request to question service', async () => { + const response = await request(app) + .get('/questions/es/1'); + + expect(response.statusCode).toBe(200); + expect(response.body[0]).toHaveProperty('question', "¿Cuál es la población de Oviedo?"); + }); + // Test /record endpoint it('should forward record request to record service', async () => { const response = await request(app) diff --git a/questionservice/question-service.js b/questionservice/question-service.js index 026feb5c..9278f131 100644 --- a/questionservice/question-service.js +++ b/questionservice/question-service.js @@ -30,6 +30,33 @@ app.get('/questions', async (req, res) => { } }); +app.get('/questions/:lang/:amount', async (req, res) => { + try { + const lang = req.params.lang; + let amount = parseInt(req.params.amount); + + if(amount > 20) + amount = 20; + + const questions = await Question.aggregate([ + {$match: {language : lang}}, //Condition + {$sample: {size:amount}} //5 random from the ones that fullfil the condition + ]); + + let jsonResult = {}; + for (let i = 0; i < questions.length; i++) { + const question = questions[i]; + jsonResult[i] = { + question : question.question, + answers : question.answers + } + } + res.json(jsonResult); + } catch (error) { + res.status(500).json({ error: 'Internal Server Error' }); + } +}); + app.get('/questions/:lang', async (req, res) => { try { const lang = req.params.lang; diff --git a/questionservice/question-service.test.js b/questionservice/question-service.test.js index 67b5ca66..a3f51c1c 100644 --- a/questionservice/question-service.test.js +++ b/questionservice/question-service.test.js @@ -12,7 +12,7 @@ beforeAll(async () => { app = require('./question-service'); //Populate db - for(let i = 0; i < 6 ; i++){ + for(let i = 0; i < 21 ; i++){ const question = new Question( { question: "¿Cuál es la población de Oviedo?", answers: [ @@ -60,4 +60,20 @@ describe('Question Service', () => { expect(response.body[0]).toHaveProperty('question', "¿Cuál es la población de Oviedo?"); expect(Object.keys(response.body).length).toBe(5); }); + + it('Should give 10 questions /questions/es/10', async () => { + + let response = await request(app).get('/questions/es/10'); + expect(response.status).toBe(200); + expect(response.body[0]).toHaveProperty('question', "¿Cuál es la población de Oviedo?"); + expect(Object.keys(response.body).length).toBe(10); + }); + + it('Should give 20 questions as the max is 20 /questions/es/21', async () => { + + let response = await request(app).get('/questions/es/21'); + expect(response.status).toBe(200); + expect(response.body[0]).toHaveProperty('question', "¿Cuál es la población de Oviedo?"); + expect(Object.keys(response.body).length).toBe(20); + }); }); \ No newline at end of file From aa34cf9367d5e6825bd4a85f92758419213bd4dd Mon Sep 17 00:00:00 2001 From: Mister-Mario Date: Mon, 15 Apr 2024 17:54:34 +0200 Subject: [PATCH 06/40] Added new endpoint to get an specific amount of questions of a specific type --- gatewayservice/gateway-service.js | 16 ++++++++++++++ gatewayservice/gateway-service.test.js | 17 ++++++++++++-- questionservice/question-service.js | 28 ++++++++++++++++++++++++ questionservice/question-service.test.js | 15 +++++++++++++ 4 files changed, 74 insertions(+), 2 deletions(-) diff --git a/gatewayservice/gateway-service.js b/gatewayservice/gateway-service.js index 12036c07..3eb2b382 100644 --- a/gatewayservice/gateway-service.js +++ b/gatewayservice/gateway-service.js @@ -58,6 +58,22 @@ app.get('/questions', async (req, res) => { } }); +app.get('/questions/:lang/:amount/:type', async (req, res) => { + try { + const lang = req.params.lang; + const amount = req.params.amount; + const type = req.params.type; + // Forward the question request to the quetion service + const questionResponse = await axios.get(questionServiceUrl+'/questions/' + lang + '/' + amount + '/' + type); + + res.json(questionResponse.data); + } catch (error) { + + res.status(error.response.status).json({ error: error.response.data.error }); + } +}); + + app.get('/questions/:lang/:amount', async (req, res) => { try { const lang = req.params.lang; diff --git a/gatewayservice/gateway-service.test.js b/gatewayservice/gateway-service.test.js index 69a771dd..8a4b0469 100644 --- a/gatewayservice/gateway-service.test.js +++ b/gatewayservice/gateway-service.test.js @@ -24,6 +24,10 @@ describe('Gateway Service', () => { if (url.endsWith('/questions')){ return Promise.resolve({ data: [{question: "¿Cuál es la población de Oviedo?", answers: ["225089","272357","267855","231841"]}] }); + } else if (url.endsWith('/questions/es/1/CAPITAL')){ + return Promise.resolve({ data: [{question: "¿Cuál es la población de Oviedo?", + answers: ["225089","272357","267855","231841"]}] }); + } else if (url.endsWith('/questions/es/1')){ return Promise.resolve({ data: [{question: "¿Cuál es la población de Oviedo?", answers: ["225089","272357","267855","231841"]}] }); @@ -75,8 +79,8 @@ describe('Gateway Service', () => { expect(response.body[0]).toHaveProperty('question', "¿Cuál es la población de Oviedo?"); }); - // Test /questions/:lang/:amount endpoint - it('should forward questions request to question service', async () => { + // Test /questions/:lang/:amount endpoint + it('should forward questions request to question service', async () => { const response = await request(app) .get('/questions/es/1'); @@ -84,6 +88,15 @@ describe('Gateway Service', () => { expect(response.body[0]).toHaveProperty('question', "¿Cuál es la población de Oviedo?"); }); + // Test /questions/:lang/:amount/:type endpoint + it('should forward questions request to question service', async () => { + const response = await request(app) + .get('/questions/es/1/CAPITAL'); + + expect(response.statusCode).toBe(200); + expect(response.body[0]).toHaveProperty('question', "¿Cuál es la población de Oviedo?"); + }); + // Test /record endpoint it('should forward record request to record service', async () => { const response = await request(app) diff --git a/questionservice/question-service.js b/questionservice/question-service.js index 9278f131..12f4c7ca 100644 --- a/questionservice/question-service.js +++ b/questionservice/question-service.js @@ -30,6 +30,34 @@ app.get('/questions', async (req, res) => { } }); +app.get('/questions/:lang/:amount/:type', async (req, res) => { + try { + const lang = req.params.lang.toString(); + let amount = parseInt(req.params.amount); + const type = req.params.type.toString(); + + if(amount > 20) + amount = 20; + + const questions = await Question.aggregate([ + {$match: {language : lang, type: type}}, //Condition + {$sample: {size:amount}} //5 random from the ones that fullfil the condition + ]); + + let jsonResult = {}; + for (let i = 0; i < questions.length; i++) { + const question = questions[i]; + jsonResult[i] = { + question : question.question, + answers : question.answers + } + } + res.json(jsonResult); + } catch (error) { + res.status(500).json({ error: 'Internal Server Error' }); + } +}); + app.get('/questions/:lang/:amount', async (req, res) => { try { const lang = req.params.lang; diff --git a/questionservice/question-service.test.js b/questionservice/question-service.test.js index a3f51c1c..6f68c54d 100644 --- a/questionservice/question-service.test.js +++ b/questionservice/question-service.test.js @@ -76,4 +76,19 @@ describe('Question Service', () => { expect(response.body[0]).toHaveProperty('question', "¿Cuál es la población de Oviedo?"); expect(Object.keys(response.body).length).toBe(20); }); + + it('Should give 10 questions /questions/es/10/POPULATION', async () => { + + let response = await request(app).get('/questions/es/10/POPULATION'); + expect(response.status).toBe(200); + expect(response.body[0]).toHaveProperty('question', "¿Cuál es la población de Oviedo?"); + expect(Object.keys(response.body).length).toBe(10); + }); + + it('Should give 0 questions /questions/es/10/CAPITAL', async () => { + + let response = await request(app).get('/questions/es/10/CAPITAL'); + expect(response.status).toBe(200); + expect(Object.keys(response.body).length).toBe(0); + }); }); \ No newline at end of file From 581febaf82ea0050c0c23f6686b7dd75b01f4cbb Mon Sep 17 00:00:00 2001 From: uo289267 Date: Mon, 15 Apr 2024 19:30:34 +0200 Subject: [PATCH 07/40] Added Game Configurator logic --- webapp/public/index.html | 1 + .../GameConfigurator/GameConfigurator.js | 32 ++++++++++++------- .../questionView/QuestionGenerator.js | 9 ++++-- .../components/questionView/QuestionView.js | 6 ++-- webapp/src/index.js | 6 ++-- 5 files changed, 34 insertions(+), 20 deletions(-) diff --git a/webapp/public/index.html b/webapp/public/index.html index aa069f27..a9a639d1 100644 --- a/webapp/public/index.html +++ b/webapp/public/index.html @@ -3,6 +3,7 @@ + :

{t("gameConfigurator.game_config")}

@@ -26,30 +33,33 @@ function GameConfigurator(){ type="number" value={numeroPreguntas} onChange={(e) => setNumeroPreguntas(e.target.value)} - min="1" + min="1" max="20" />

- +

{t("gameConfigurator.rules_competi")}

{/* Botones para jugar un juego personalizado o competitivo */} - -
); } -function ButtonCustomized({t, type, number}){ - return( - - ); + +function ButtonCustomized({t,handleClick}) { + + return ( + + ); } + function ButtonCompetitive({t}){ //llamar setTipoPregunta COMPETITIVE - return( - + return ( + +

{t("gameConfigurator.play_competi")}

+ ); } diff --git a/webapp/src/components/questionView/QuestionGenerator.js b/webapp/src/components/questionView/QuestionGenerator.js index ff9058de..8ea479e8 100644 --- a/webapp/src/components/questionView/QuestionGenerator.js +++ b/webapp/src/components/questionView/QuestionGenerator.js @@ -8,12 +8,13 @@ class QuestionGenerator{ } - async generateQuestions(lang) { + async generateQuestions(lang, type, amount) { try { //const response = await fetch(this.apiUrl); //const receivedQuestions = await response.json(); //Mockup + console.log("type: "+type+" amount: "+amount) const receivedQuestions = JSON.parse('{"0":{"question":"¿Cuál es la población de Oviedo?","answers":["225089","191325","220587","121548"]},'+ '"1":{"question":"¿Cuál es la población de Gijón?","answers":["275274","159658","233982","305554"]},'+ '"2":{"question":"¿Cuál es la población de Avilés?","answers":["82568","115595","41284","122200"]},'+ @@ -35,7 +36,11 @@ class QuestionGenerator{ /* try { - const response = await axios.get(this.apiUrl + '/' + lang); + if(type==="COMPETITIVE"){ + const response = await axios.get(this.apiUrl + '/' + lang); + }else{ + const response = await axios.get(this.apiUrl + '/' + lang + '/' +amount + '/' + type); + } const receivedQuestions = await response.data; let i = 0; var questions = []; diff --git a/webapp/src/components/questionView/QuestionView.js b/webapp/src/components/questionView/QuestionView.js index cf4ea79c..156e3828 100644 --- a/webapp/src/components/questionView/QuestionView.js +++ b/webapp/src/components/questionView/QuestionView.js @@ -14,16 +14,16 @@ import BackButtonToGameMenu from '../fragments/BackButtonToGameMenu'; const creationHistoricalRecord = new CreationHistoricalRecord(); const questionGenerator = new QuestionGenerator(); var points = 0; -function QuestionView(){ +function QuestionView({type= "COMPETITIVE", amount=5}){ const [numQuestion, setnumQuestion] = useState(-1); const [questions, setQuestions] = useState(null); const[t, i18n] = useTranslation("global"); const {user} = useUserContext(); - + const generateQuestions = async (numQuestion) => { if (numQuestion < 0) { try { - var generatedQuestions = await questionGenerator.generateQuestions(i18n.language); + var generatedQuestions = await questionGenerator.generateQuestions(i18n.language, type, amount); setQuestions(generatedQuestions); setnumQuestion(0); } catch (error) { diff --git a/webapp/src/index.js b/webapp/src/index.js index 05cbd873..103dc805 100644 --- a/webapp/src/index.js +++ b/webapp/src/index.js @@ -32,16 +32,14 @@ i18next.init({ const root = ReactDOM.createRoot(document.getElementById('root')); root.render( + - - - - {/* envolviendo la app con el sistema de traducciones */} + ); // If you want to start measuring performance in your app, pass a function From 759076b7381f4db377699ccd0a4b0e8cda8870ea Mon Sep 17 00:00:00 2001 From: uo289267 Date: Mon, 15 Apr 2024 21:26:18 +0200 Subject: [PATCH 08/40] Added style to game configurator --- .../GameConfigurator/GameConfigurator.js | 19 ++++--- webapp/src/custom.css | 50 ++++++++++++++++++- webapp/src/translations/en/global.json | 4 +- webapp/src/translations/es/global.json | 8 +-- 4 files changed, 69 insertions(+), 12 deletions(-) diff --git a/webapp/src/components/GameConfigurator/GameConfigurator.js b/webapp/src/components/GameConfigurator/GameConfigurator.js index 558b85f6..1c592294 100644 --- a/webapp/src/components/GameConfigurator/GameConfigurator.js +++ b/webapp/src/components/GameConfigurator/GameConfigurator.js @@ -3,6 +3,7 @@ import React, { useState } from 'react'; import {useTranslation} from "react-i18next"; import { Link } from "react-router-dom"; import QuestionView from '../questionView/QuestionView' +import BackButtonToGameMenu from '../fragments/BackButtonToGameMenu'; function GameConfigurator(){ const [tipoPregunta, setTipoPregunta] = useState('POPULATION'); @@ -15,11 +16,12 @@ function GameConfigurator(){ } return ( clickedForNewGame ? : -
+
+

{t("gameConfigurator.game_config")}

- +

{t("gameConfigurator.custo_game")}

- setTipoPregunta(e.target.value)}> @@ -29,7 +31,7 @@ function GameConfigurator(){ {/* Spinner para seleccionar el número de preguntas */} - setNumeroPreguntas(e.target.value)} @@ -38,24 +40,27 @@ function GameConfigurator(){



+
+

+

{t("gameConfigurator.competi_game")}

{t("gameConfigurator.rules_competi")}

{/* Botones para jugar un juego personalizado o competitivo */} +
); } function ButtonCustomized({t,handleClick}) { - return ( - + ); } function ButtonCompetitive({t}){ - //llamar setTipoPregunta COMPETITIVE + return (

{t("gameConfigurator.play_competi")}

diff --git a/webapp/src/custom.css b/webapp/src/custom.css index 39d91582..3c30da2f 100644 --- a/webapp/src/custom.css +++ b/webapp/src/custom.css @@ -1231,7 +1231,8 @@ svg { font-size: 1em; color: black; font-weight: 700; - margin: 1em; + margin-top: 3em; + margin-bottom: 3em; text-decoration: none; } @@ -1299,3 +1300,50 @@ svg { h2{ text-align: center; } +/*--------------------------------------------------Configurator---------------------------------*/ +/* Estilo para el elemento select */ +.select-style { + width: 200px; + height: 35px; + border: 1px solid #ccc; + border-radius: 5px; + background-color: #f8f8f8; + padding: 5px; + font-size: 16px; + color: #333; +} + +.select-style:focus { + border-color: #6e9ecf; + outline: none; +} + +/* Estilo para el spinner */ +.spinner-style { + width: 40px; + height: 30px; + border: 1px solid #ccc; + background-color: #f8f8f8; + border-radius: 5px; + font-size: medium; +} +.GameConfiguratorDiv> * { + margin-top: 10px; + margin-bottom: 10px; +} +.GameConfiguratorDiv { + left: 50%; + top: 50%; +} +.hr-style { + border: 0; + border-top: 1px solid #8e888885; /* Color gris */ + margin: 20px 0; /* Espacio antes y después de la línea */ +} + +.GameConfiguratorDiv h2 { + text-align: left; /* Alinea el texto a la izquierda */ + margin-top:20px; + margin-bottom:20px; +} + diff --git a/webapp/src/translations/en/global.json b/webapp/src/translations/en/global.json index 2a2963dd..28c2a341 100644 --- a/webapp/src/translations/en/global.json +++ b/webapp/src/translations/en/global.json @@ -81,7 +81,9 @@ "option_population":"Population", "option_capital":"Capital", "option_language":"Language", - "option_size":"Size" + "option_size":"Size", + "custo_game":"Create custom game", + "competi_game":"Play Competitive" } } diff --git a/webapp/src/translations/es/global.json b/webapp/src/translations/es/global.json index 2f855737..32f9e22a 100644 --- a/webapp/src/translations/es/global.json +++ b/webapp/src/translations/es/global.json @@ -76,15 +76,17 @@ }, "gameConfigurator":{ "game_config":"Configuración del Juego", - "type_quest":"Tipo de Pregunta", - "num_quest":"Numero de Preguntas", + "type_quest":"Tipo de Pregunta : ", + "num_quest":"Número de Preguntas : ", "play_custom":"Jugar personalizado", "rules_competi":"Jugar con todo tipo de preguntas siendo estas 5", "play_competi":"Jugar Competitivo", "option_population":"Población", "option_capital":"Capital", "option_language":"Lenguaje", - "option_size":"Extensión" + "option_size":"Extensión", + "custo_game":"Crea una partida personalizada", + "competi_game":"Juega en modo Competitivo" } } From d31b5ab6ea8b8f8761a1bfa351c6129431f818ac Mon Sep 17 00:00:00 2001 From: Mister-Mario Date: Mon, 15 Apr 2024 22:57:56 +0200 Subject: [PATCH 09/40] Added enpoints for getting the ranking information of the competitive games --- gatewayservice/gateway-service.js | 21 ++++ gatewayservice/gateway-service.test.js | 24 ++++ questionservice/question-service.js | 20 ++-- questionservice/question-service.test.js | 27 ++++- users/recordservice/record-model.js | 18 +-- users/recordservice/record-service.js | 60 +++++++++- users/recordservice/record-service.test.js | 112 +++++++++++++++++- .../HistoricalData/HistoryRecordRetriever.js | 10 ++ .../questionView/CreationHistoricalRecord.js | 4 + .../questionView/QuestionGenerator.js | 12 +- .../components/questionView/QuestionView.js | 1 + 11 files changed, 276 insertions(+), 33 deletions(-) diff --git a/gatewayservice/gateway-service.js b/gatewayservice/gateway-service.js index 3eb2b382..983670c6 100644 --- a/gatewayservice/gateway-service.js +++ b/gatewayservice/gateway-service.js @@ -111,6 +111,27 @@ app.post('/record', async(req, res) => { } }); +app.get('/record/ranking/top10', async(req, res)=>{ + try { + // Forward the record request to the record service + const recordResponse = await axios.get(recordServiceUrl + '/record/ranking/top10'); + res.json(recordResponse.data); + } catch (error) { + res.send(error); + } +}); + +app.get('/record/ranking/:user', async(req, res)=>{ + try { + const user = req.params.user; + // Forward the record request to the record service + const recordResponse = await axios.get(recordServiceUrl + '/record/ranking/' + user); + res.json(recordResponse.data); + } catch (error) { + res.send(error); + } +}); + app.get('/record/:user', async(req, res)=>{ try { const user = req.params.user; diff --git a/gatewayservice/gateway-service.test.js b/gatewayservice/gateway-service.test.js index 8a4b0469..57c63816 100644 --- a/gatewayservice/gateway-service.test.js +++ b/gatewayservice/gateway-service.test.js @@ -37,6 +37,12 @@ describe('Gateway Service', () => { } else if(url.endsWith('/record/testuser')){ //Dont need to check a good record just that it redirects the call return Promise.resolve({data : {record:'undefined'}}) + } else if(url.endsWith('/record/ranking/top10')){ + //Dont need to check a good record just that it redirects the call + return Promise.resolve({data : {record:'undefined'}}) + } else if(url.endsWith('/record/ranking/testuser')){ + //Dont need to check a good record just that it redirects the call + return Promise.resolve({data : {record:'undefined'}}) } }); @@ -114,4 +120,22 @@ describe('Gateway Service', () => { expect(response.statusCode).toBe(200); expect(response.body).toHaveProperty('record', "undefined"); }); + + // Test /record/ranking/:user endpoint + it('should forward record request to record service', async () => { + const response = await request(app) + .get('/record/ranking/testuser'); + + expect(response.statusCode).toBe(200); + expect(response.body).toHaveProperty('record', "undefined"); + }); + + // Test /record/ranking/top10 endpoint + it('should forward record request to record service', async () => { + const response = await request(app) + .get('/record/ranking/top10'); + + expect(response.statusCode).toBe(200); + expect(response.body).toHaveProperty('record', "undefined"); + }); }); \ No newline at end of file diff --git a/questionservice/question-service.js b/questionservice/question-service.js index 12f4c7ca..effc5ac7 100644 --- a/questionservice/question-service.js +++ b/questionservice/question-service.js @@ -33,15 +33,15 @@ app.get('/questions', async (req, res) => { app.get('/questions/:lang/:amount/:type', async (req, res) => { try { const lang = req.params.lang.toString(); - let amount = parseInt(req.params.amount); + let amount = checkAmount(parseInt(req.params.amount)); const type = req.params.type.toString(); - if(amount > 20) - amount = 20; + if(amount > 20 || amount < 1) + amount = 5; const questions = await Question.aggregate([ {$match: {language : lang, type: type}}, //Condition - {$sample: {size:amount}} //5 random from the ones that fullfil the condition + {$sample: {size:amount}} ]); let jsonResult = {}; @@ -61,14 +61,13 @@ app.get('/questions/:lang/:amount/:type', async (req, res) => { app.get('/questions/:lang/:amount', async (req, res) => { try { const lang = req.params.lang; - let amount = parseInt(req.params.amount); + let amount = checkAmount(parseInt(req.params.amount)); - if(amount > 20) - amount = 20; + const questions = await Question.aggregate([ {$match: {language : lang}}, //Condition - {$sample: {size:amount}} //5 random from the ones that fullfil the condition + {$sample: {size:amount}} ]); let jsonResult = {}; @@ -108,6 +107,11 @@ app.get('/questions/:lang', async (req, res) => { } }); +function checkAmount(amount){ + if(amount > 20 || amount < 1) + return 5; + return amount; +} const server = app.listen(port, () => { console.log(`Question Service listening at http://localhost:${port}`); diff --git a/questionservice/question-service.test.js b/questionservice/question-service.test.js index 6f68c54d..35b70eae 100644 --- a/questionservice/question-service.test.js +++ b/questionservice/question-service.test.js @@ -61,22 +61,39 @@ describe('Question Service', () => { expect(Object.keys(response.body).length).toBe(5); }); - it('Should give 10 questions /questions/es/10', async () => { - let response = await request(app).get('/questions/es/10'); + it('Should give 20 questions /questions/es/20', async () => { + + let response = await request(app).get('/questions/es/20'); expect(response.status).toBe(200); expect(response.body[0]).toHaveProperty('question', "¿Cuál es la población de Oviedo?"); - expect(Object.keys(response.body).length).toBe(10); + expect(Object.keys(response.body).length).toBe(20); }); - it('Should give 20 questions as the max is 20 /questions/es/21', async () => { + it('Should give 1 questions /questions/es/1', async () => { - let response = await request(app).get('/questions/es/21'); + let response = await request(app).get('/questions/es/20'); expect(response.status).toBe(200); expect(response.body[0]).toHaveProperty('question', "¿Cuál es la población de Oviedo?"); expect(Object.keys(response.body).length).toBe(20); }); + it('Should give 5 questions as the max is 20 /questions/es/21', async () => { + + let response = await request(app).get('/questions/es/21'); + expect(response.status).toBe(200); + expect(response.body[0]).toHaveProperty('question', "¿Cuál es la población de Oviedo?"); + expect(Object.keys(response.body).length).toBe(5); + }); + + it('Should give 5 questions as the min is 1 /questions/es/0', async () => { + + let response = await request(app).get('/questions/es/0'); + expect(response.status).toBe(200); + expect(response.body[0]).toHaveProperty('question', "¿Cuál es la población de Oviedo?"); + expect(Object.keys(response.body).length).toBe(5); + }); + it('Should give 10 questions /questions/es/10/POPULATION', async () => { let response = await request(app).get('/questions/es/10/POPULATION'); diff --git a/users/recordservice/record-model.js b/users/recordservice/record-model.js index a7a30d76..a302df52 100644 --- a/users/recordservice/record-model.js +++ b/users/recordservice/record-model.js @@ -1,16 +1,18 @@ const mongoose = require('mongoose'); +const { Schema } = mongoose; -const recordSchema = new mongoose.Schema({ - user: String, +const recordSchema = new Schema({ + user: { type: String, required: true }, games: [{ questions: [{ - question: String, - answers: [String], - answerGiven: String, - correctAnswer: String + question: { type: String, required: true }, + answers: { type: [String], required: true }, + answerGiven: { type: String, required: true }, + correctAnswer: { type: String, required: true } }], - points: Number, - date: String + points: { type: Number, required: true }, + date: { type: String, required: true }, + competitive: { type: Boolean, required: true } }] }); const Record = mongoose.model('Record', recordSchema); diff --git a/users/recordservice/record-service.js b/users/recordservice/record-service.js index dd383984..4528a2b6 100644 --- a/users/recordservice/record-service.js +++ b/users/recordservice/record-service.js @@ -1,6 +1,5 @@ const express = require('express'); const mongoose = require('mongoose'); -const bcrypt = require('bcrypt'); const bodyParser = require('body-parser'); const Record = require('./record-model') @@ -18,14 +17,12 @@ mongoose.connect(mongoUri); app.post('/record', async (req, res) => { const user = req.body.user; const game = req.body.game; - console.log(user) - console.log(game) if(user && game){ let record = await Record.findOne({ user : user }); if(record){ //If it exits record.games.push(game); } - else{ //Lo creamos + else{ //We make it record = new Record({ user:user, games:[game] @@ -45,9 +42,62 @@ app.post('/record', async (req, res) => { }); +app.get('/record/ranking/top10', async (req, res) => { + try { + const usersCompetitiveStats = await Record.aggregate([ + // Unwind the games array to work with each game separately + { $unwind: "$games" }, + // Match only competitive games + { $match: { "games.competitive": true } }, + // Group by user and calculate total points and total competitive games per user + { + $group: { + _id: "$user", + totalPoints: { $sum: "$games.points" }, + totalCompetitiveGames: { $sum: 1 } // Count the number of competitive games + } + }, + // Sort by total points in descending order (top 1 will have the highest points) + { $sort: { totalPoints: -1 } }, + // Limit to the top 10 + { $limit: 10 } + ]); + + res.json({usersCompetitiveStats: usersCompetitiveStats }); + } catch (err) { + res.status(500).send(); + } +}); + +app.get('/record/ranking/:user', async (req, res) => { + try { + const user = req.params.user.toString(); + //Gives back an array of 1 user + const userCompetitiveStats = await Record.aggregate([ + { $match: { user: user } }, + // Unwind the games array to work with each game separately + { $unwind: "$games" }, + // Match only competitive games + { $match: { "games.competitive": true } }, + // Group by user and calculate total points and total competitive games per user + { + $group: { + _id: "$user", + totalPoints: { $sum: "$games.points" }, + totalCompetitiveGames: { $sum: 1 } // Count the number of competitive games + } + } + ]); + + res.json({userCompetitiveStats: userCompetitiveStats[0] }); + } catch (err) { + res.status(500).send(); + } +}); + app.get('/record/:user', async (req, res) => { try { - const recordFound = await Record.findOne({ user: req.params.user }, 'games'); + const recordFound = await Record.findOne({ user: req.params.user.toString() }, 'games'); if (!recordFound) { res.json({record: "undefined" }); } else { diff --git a/users/recordservice/record-service.test.js b/users/recordservice/record-service.test.js index d2c2ca6e..0346bbc2 100644 --- a/users/recordservice/record-service.test.js +++ b/users/recordservice/record-service.test.js @@ -1,5 +1,6 @@ const request = require('supertest'); const { MongoMemoryServer } = require('mongodb-memory-server'); +const Record = require('./record-model') let mongoServer; let app; @@ -9,6 +10,8 @@ beforeAll(async () => { const mongoUri = mongoServer.getUri(); process.env.MONGODB_URI = mongoUri; app = require('./record-service'); + + await populateDatabase(); }); afterAll(async () => { @@ -99,6 +102,33 @@ describe('Record Service', () => { response = await request(app).post('/record').send(newUser); expect(response.status).toBe(400); + //Data lacks competitive field + newUser = { + user:"testuser", + game: + { + "questions": [ + { + "question": "¿Cuál es el río más largo del mundo?", + "answers": ["Nilo", "Amazonas", "Yangtsé", "Misisipi"], + "answerGiven": "Amazonas", + "correctAnswer": "Amazonas" + }, + { + "question": "¿Cuál es el elemento más abundante en la corteza terrestre?", + "answers": ["Hierro", "Oxígeno", "Silicio", "Aluminio"], + "answerGiven": "Oxígeno", + "correctAnswer": "Oxígeno" + } + ], + "points": 2500, + "date": "02/02/24" + } + }; + + response = await request(app).post('/record').send(newUser); + expect(response.status).toBe(500); + }); it('should add a new record on POST /record', async () => { const newUser = { @@ -120,7 +150,8 @@ describe('Record Service', () => { } ], "points": 2500, - "date": "02/02/24" + "date": "02/02/24", + "competitive": false } }; @@ -161,7 +192,8 @@ describe('Record Service', () => { } ], "points": 3000, - "date": "03/03/24" + "date": "03/03/24", + "competitive": false } }; @@ -174,4 +206,78 @@ describe('Record Service', () => { expect(responseGet.body.record.games[1]).toHaveProperty('date', '03/03/24'); }); -}); \ No newline at end of file + + it('should get back on GET /record/testuser', async () => { + const responseGet = await request(app).get('/record/testuser'); + expect(responseGet.status).toBe(200); + expect(responseGet.body.record.games[0]).toHaveProperty('date', '02/02/24'); + }); + + it('should get back on GET /record/testuser', async () => { + const responseGet = await request(app).get('/record/testuser'); + expect(responseGet.status).toBe(200); + expect(responseGet.body.record.games[0]).toHaveProperty('date', '02/02/24'); + }); + + it('should get back on GET /record/ranking/top10', async () => { + const responseGet = await request(app).get('/record/ranking/top10'); + console.log(responseGet.body) + expect(responseGet.status).toBe(200); + const usersStats = responseGet.body.usersCompetitiveStats; + expect(usersStats.length).toBe(10); //Only top 10 + + //Ordered by points + expect(usersStats[0]).toHaveProperty('_id', 'user10'); + expect(usersStats[9]).toHaveProperty('_id', 'user1'); + + expect(usersStats[0]).toHaveProperty('totalCompetitiveGames', 2); + expect(usersStats[0]).toHaveProperty('totalPoints', 200); + }); + + it('should get back on GET /record/ranking/user1', async () => { + const responseGet = await request(app).get('/record/ranking/user1'); + expect(responseGet.status).toBe(200); + const userStats = responseGet.body.userCompetitiveStats; + + expect(userStats).toHaveProperty('_id', 'user1'); + expect(userStats).toHaveProperty('totalCompetitiveGames', 2); + expect(userStats).toHaveProperty('totalPoints', 20); //i * 10 * totalCompetitiveGames , i = 2 + }); +}); + + + +async function populateDatabase() { + try { + // Generate 10 users + for (let i = 1; i <= 10; i++) { + const user = `user${i}`; + const games = []; + + // Generate 3 games for each user + for (let j = 1; j <= 3; j++) { + const game = { + questions: [ + { + question: `Question ${j} for ${user}`, + answers: ["Answer 1", "Answer 2", "Answer 3", "Answer 4"], + answerGiven: "Answer 1", + correctAnswer: "Answer 1" + } + ], + points: i * 10, + date: "04/01/2024", + competitive: j <= 2 ? true : false // Only 2 games are competitive + }; + games.push(game); + } + + // Guardar el usuario en la base de datos + await Record.create({ user, games }); + } + + console.log('Database populated successfully'); + } catch (error) { + console.error('Error populating database:', error); + } +} \ No newline at end of file diff --git a/webapp/src/components/HistoricalData/HistoryRecordRetriever.js b/webapp/src/components/HistoricalData/HistoryRecordRetriever.js index c240853e..5b837321 100644 --- a/webapp/src/components/HistoricalData/HistoryRecordRetriever.js +++ b/webapp/src/components/HistoricalData/HistoryRecordRetriever.js @@ -8,6 +8,16 @@ class HistoryRecordRetriever{ } async getRecords(user) { + /* + try { + const response = await axios.get(this.apiUrl + '/' + user); + const receivedRecords = await response.data; + return receivedRecords.record; + } catch (error) { + console.log(error) + throw new Error(error); + + }*/ return { userId: user, games: [ diff --git a/webapp/src/components/questionView/CreationHistoricalRecord.js b/webapp/src/components/questionView/CreationHistoricalRecord.js index 9da8f624..68ceb4f5 100644 --- a/webapp/src/components/questionView/CreationHistoricalRecord.js +++ b/webapp/src/components/questionView/CreationHistoricalRecord.js @@ -29,6 +29,10 @@ class CreationHistoricalRecord{ this.record.game.date = date; } + setCompetitive(isCompetitive){ + this.record.game.competitive = isCompetitive; + } + getRecord() { return this.record; } diff --git a/webapp/src/components/questionView/QuestionGenerator.js b/webapp/src/components/questionView/QuestionGenerator.js index 8ea479e8..2add8f05 100644 --- a/webapp/src/components/questionView/QuestionGenerator.js +++ b/webapp/src/components/questionView/QuestionGenerator.js @@ -9,6 +9,7 @@ class QuestionGenerator{ } async generateQuestions(lang, type, amount) { + try { //const response = await fetch(this.apiUrl); //const receivedQuestions = await response.json(); @@ -33,13 +34,15 @@ class QuestionGenerator{ } catch (error) { throw new Error(error); } - + + /* try { + let response; if(type==="COMPETITIVE"){ - const response = await axios.get(this.apiUrl + '/' + lang); + response = await axios.get(this.apiUrl + '/' + lang); }else{ - const response = await axios.get(this.apiUrl + '/' + lang + '/' +amount + '/' + type); + response = await axios.get(this.apiUrl + '/' + lang + '/' +amount + '/' + type); } const receivedQuestions = await response.data; let i = 0; @@ -51,7 +54,8 @@ class QuestionGenerator{ return questions; } catch (error) { throw new Error(error); - }*/ + } + */ } } diff --git a/webapp/src/components/questionView/QuestionView.js b/webapp/src/components/questionView/QuestionView.js index 156e3828..37ebfc3f 100644 --- a/webapp/src/components/questionView/QuestionView.js +++ b/webapp/src/components/questionView/QuestionView.js @@ -91,6 +91,7 @@ function QuestionView({type= "COMPETITIVE", amount=5}){ //Last question sends the record if(!(numQuestion < questions.length - 1)){ + creationHistoricalRecord.setCompetitive(type === 'COMPETITIVE'); creationHistoricalRecord.setDate(Date.now()); creationHistoricalRecord.setPoints(points); creationHistoricalRecord.sendRecord(user.username); From a2b2f6a40a7cb9b1c79887720c1da2b3bda30c04 Mon Sep 17 00:00:00 2001 From: Mister-Mario Date: Mon, 15 Apr 2024 22:58:40 +0200 Subject: [PATCH 10/40] Removed console log --- users/recordservice/record-service.test.js | 1 - 1 file changed, 1 deletion(-) diff --git a/users/recordservice/record-service.test.js b/users/recordservice/record-service.test.js index 0346bbc2..a42d1978 100644 --- a/users/recordservice/record-service.test.js +++ b/users/recordservice/record-service.test.js @@ -221,7 +221,6 @@ describe('Record Service', () => { it('should get back on GET /record/ranking/top10', async () => { const responseGet = await request(app).get('/record/ranking/top10'); - console.log(responseGet.body) expect(responseGet.status).toBe(200); const usersStats = responseGet.body.usersCompetitiveStats; expect(usersStats.length).toBe(10); //Only top 10 From 9451f8787c6ac1925cd8e203b8f5fc01ded98464 Mon Sep 17 00:00:00 2001 From: Mister-Mario Date: Mon, 15 Apr 2024 23:04:15 +0200 Subject: [PATCH 11/40] Commented mocks in order to check sonar cloud evaluation --- .../components/HistoricalData/HistoryRecordRetriever.js | 7 ++++--- webapp/src/components/questionView/QuestionGenerator.js | 6 +++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/webapp/src/components/HistoricalData/HistoryRecordRetriever.js b/webapp/src/components/HistoricalData/HistoryRecordRetriever.js index 5b837321..86fad6d7 100644 --- a/webapp/src/components/HistoricalData/HistoryRecordRetriever.js +++ b/webapp/src/components/HistoricalData/HistoryRecordRetriever.js @@ -8,7 +8,7 @@ class HistoryRecordRetriever{ } async getRecords(user) { - /* + try { const response = await axios.get(this.apiUrl + '/' + user); const receivedRecords = await response.data; @@ -17,7 +17,8 @@ class HistoryRecordRetriever{ console.log(error) throw new Error(error); - }*/ + } + /* return { userId: user, games: [ @@ -82,7 +83,7 @@ class HistoryRecordRetriever{ date: "03/02/24" } ] - }; + };*/ } diff --git a/webapp/src/components/questionView/QuestionGenerator.js b/webapp/src/components/questionView/QuestionGenerator.js index 2add8f05..b2c278e3 100644 --- a/webapp/src/components/questionView/QuestionGenerator.js +++ b/webapp/src/components/questionView/QuestionGenerator.js @@ -9,7 +9,7 @@ class QuestionGenerator{ } async generateQuestions(lang, type, amount) { - + /* try { //const response = await fetch(this.apiUrl); //const receivedQuestions = await response.json(); @@ -34,9 +34,9 @@ class QuestionGenerator{ } catch (error) { throw new Error(error); } + */ - /* try { let response; if(type==="COMPETITIVE"){ @@ -55,7 +55,7 @@ class QuestionGenerator{ } catch (error) { throw new Error(error); } - */ + } } From ccfd4833832f2b881d1f27be8b0cfe38a3f485be Mon Sep 17 00:00:00 2001 From: Mister-Mario Date: Mon, 15 Apr 2024 23:19:03 +0200 Subject: [PATCH 12/40] Fixed a bug that was already solved in another branch --- webapp/src/components/HistoricalData/HistoricalView.js | 3 ++- webapp/src/components/HistoricalData/HistoryRecordRetriever.js | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/webapp/src/components/HistoricalData/HistoricalView.js b/webapp/src/components/HistoricalData/HistoricalView.js index 9812acc7..266efe26 100644 --- a/webapp/src/components/HistoricalData/HistoricalView.js +++ b/webapp/src/components/HistoricalData/HistoricalView.js @@ -16,9 +16,10 @@ export default function HistoricalView() { const getRecords = async ()=>{ try { - var jsonRecords = await retriever.getRecords("user"); + var jsonRecords = await retriever.getRecords(user.username); var recordsArray = jsonRecords.games; setRecords(recordsArray); + console.log(recordsArray) } catch (error) { console.log(error); } diff --git a/webapp/src/components/HistoricalData/HistoryRecordRetriever.js b/webapp/src/components/HistoricalData/HistoryRecordRetriever.js index 86fad6d7..02456549 100644 --- a/webapp/src/components/HistoricalData/HistoryRecordRetriever.js +++ b/webapp/src/components/HistoricalData/HistoryRecordRetriever.js @@ -8,7 +8,7 @@ class HistoryRecordRetriever{ } async getRecords(user) { - + console.log(user) try { const response = await axios.get(this.apiUrl + '/' + user); const receivedRecords = await response.data; From 18636fe0feab6f401bca92654aa2b1dd216cded1 Mon Sep 17 00:00:00 2001 From: Mister-Mario Date: Mon, 15 Apr 2024 23:19:30 +0200 Subject: [PATCH 13/40] Removed console log --- webapp/src/components/HistoricalData/HistoricalView.js | 1 - webapp/src/components/HistoricalData/HistoryRecordRetriever.js | 1 - 2 files changed, 2 deletions(-) diff --git a/webapp/src/components/HistoricalData/HistoricalView.js b/webapp/src/components/HistoricalData/HistoricalView.js index 266efe26..66dc8a2d 100644 --- a/webapp/src/components/HistoricalData/HistoricalView.js +++ b/webapp/src/components/HistoricalData/HistoricalView.js @@ -19,7 +19,6 @@ export default function HistoricalView() { var jsonRecords = await retriever.getRecords(user.username); var recordsArray = jsonRecords.games; setRecords(recordsArray); - console.log(recordsArray) } catch (error) { console.log(error); } diff --git a/webapp/src/components/HistoricalData/HistoryRecordRetriever.js b/webapp/src/components/HistoricalData/HistoryRecordRetriever.js index 02456549..0e65d6ff 100644 --- a/webapp/src/components/HistoricalData/HistoryRecordRetriever.js +++ b/webapp/src/components/HistoricalData/HistoryRecordRetriever.js @@ -8,7 +8,6 @@ class HistoryRecordRetriever{ } async getRecords(user) { - console.log(user) try { const response = await axios.get(this.apiUrl + '/' + user); const receivedRecords = await response.data; From ab9b42c7e3044deeeb769b6f753089389b7f27f4 Mon Sep 17 00:00:00 2001 From: Mister-Mario Date: Mon, 15 Apr 2024 23:30:27 +0200 Subject: [PATCH 14/40] Refactored the gateway-service tests for removing duplicated code --- gatewayservice/gateway-service.js | 12 ++--- gatewayservice/gateway-service.test.js | 64 +++++++++++++------------- 2 files changed, 39 insertions(+), 37 deletions(-) diff --git a/gatewayservice/gateway-service.js b/gatewayservice/gateway-service.js index 983670c6..317c55ae 100644 --- a/gatewayservice/gateway-service.js +++ b/gatewayservice/gateway-service.js @@ -60,9 +60,9 @@ app.get('/questions', async (req, res) => { app.get('/questions/:lang/:amount/:type', async (req, res) => { try { - const lang = req.params.lang; - const amount = req.params.amount; - const type = req.params.type; + const lang = req.params.lang.toString(); + const amount = req.params.amount.toString(); + const type = req.params.type.toString(); // Forward the question request to the quetion service const questionResponse = await axios.get(questionServiceUrl+'/questions/' + lang + '/' + amount + '/' + type); @@ -76,8 +76,8 @@ app.get('/questions/:lang/:amount/:type', async (req, res) => { app.get('/questions/:lang/:amount', async (req, res) => { try { - const lang = req.params.lang; - const amount = req.params.amount; + const lang = req.params.lang.toString(); + const amount = req.params.amount.toString(); // Forward the question request to the quetion service const questionResponse = await axios.get(questionServiceUrl+'/questions/' + lang + '/' + amount); @@ -90,7 +90,7 @@ app.get('/questions/:lang/:amount', async (req, res) => { app.get('/questions/:lang', async (req, res) => { try { - const lang = req.params.lang; + const lang = req.params.lang.toString(); // Forward the question request to the quetion service const questionResponse = await axios.get(questionServiceUrl+'/questions/' + lang); diff --git a/gatewayservice/gateway-service.test.js b/gatewayservice/gateway-service.test.js index 57c63816..971fead9 100644 --- a/gatewayservice/gateway-service.test.js +++ b/gatewayservice/gateway-service.test.js @@ -20,29 +20,28 @@ describe('Gateway Service', () => { } }); + const question = { data: [{question: "¿Cuál es la población de Oviedo?", + answers: ["225089","272357","267855","231841"]}] }; + + //Dont need to check a good record just that it redirects the call + const record = {data : {record:'undefined'}}; + axios.get.mockImplementation((url, data) => { if (url.endsWith('/questions')){ - return Promise.resolve({ data: [{question: "¿Cuál es la población de Oviedo?", - answers: ["225089","272357","267855","231841"]}] }); + return Promise.resolve(question); } else if (url.endsWith('/questions/es/1/CAPITAL')){ - return Promise.resolve({ data: [{question: "¿Cuál es la población de Oviedo?", - answers: ["225089","272357","267855","231841"]}] }); - + return Promise.resolve(question); } else if (url.endsWith('/questions/es/1')){ - return Promise.resolve({ data: [{question: "¿Cuál es la población de Oviedo?", - answers: ["225089","272357","267855","231841"]}] }); + return Promise.resolve(question); } else if (url.endsWith('/questions/es')){ - return Promise.resolve({ data: [{question: "¿Cuál es la población de Oviedo?", - answers: ["225089","272357","267855","231841"]}] }); + return Promise.resolve(question); + } else if(url.endsWith('/record/testuser')){ - //Dont need to check a good record just that it redirects the call - return Promise.resolve({data : {record:'undefined'}}) + return Promise.resolve(record) } else if(url.endsWith('/record/ranking/top10')){ - //Dont need to check a good record just that it redirects the call - return Promise.resolve({data : {record:'undefined'}}) + return Promise.resolve(record) } else if(url.endsWith('/record/ranking/testuser')){ - //Dont need to check a good record just that it redirects the call - return Promise.resolve({data : {record:'undefined'}}) + return Promise.resolve(record) } }); @@ -72,8 +71,7 @@ describe('Gateway Service', () => { const response = await request(app) .get('/questions'); - expect(response.statusCode).toBe(200); - expect(response.body[0]).toHaveProperty('question', "¿Cuál es la población de Oviedo?"); + checkQuestion(response); }); // Test /questions/:lang endpoint @@ -81,8 +79,7 @@ describe('Gateway Service', () => { const response = await request(app) .get('/questions/es'); - expect(response.statusCode).toBe(200); - expect(response.body[0]).toHaveProperty('question', "¿Cuál es la población de Oviedo?"); + checkQuestion(response); }); // Test /questions/:lang/:amount endpoint @@ -90,8 +87,7 @@ describe('Gateway Service', () => { const response = await request(app) .get('/questions/es/1'); - expect(response.statusCode).toBe(200); - expect(response.body[0]).toHaveProperty('question', "¿Cuál es la población de Oviedo?"); + checkQuestion(response); }); // Test /questions/:lang/:amount/:type endpoint @@ -99,8 +95,7 @@ describe('Gateway Service', () => { const response = await request(app) .get('/questions/es/1/CAPITAL'); - expect(response.statusCode).toBe(200); - expect(response.body[0]).toHaveProperty('question', "¿Cuál es la población de Oviedo?"); + checkQuestion(response); }); // Test /record endpoint @@ -117,8 +112,7 @@ describe('Gateway Service', () => { const response = await request(app) .get('/record/testuser'); - expect(response.statusCode).toBe(200); - expect(response.body).toHaveProperty('record', "undefined"); + checkRecord(response); }); // Test /record/ranking/:user endpoint @@ -126,16 +120,24 @@ describe('Gateway Service', () => { const response = await request(app) .get('/record/ranking/testuser'); - expect(response.statusCode).toBe(200); - expect(response.body).toHaveProperty('record', "undefined"); + checkRecord(response); }); // Test /record/ranking/top10 endpoint it('should forward record request to record service', async () => { const response = await request(app) .get('/record/ranking/top10'); - - expect(response.statusCode).toBe(200); - expect(response.body).toHaveProperty('record', "undefined"); + checkRecord(response); + }); -}); \ No newline at end of file +}); + +function checkRecord(response){ + expect(response.statusCode).toBe(200); + expect(response.body).toHaveProperty('record', "undefined"); +} + +function checkQuestion(response){ + expect(response.statusCode).toBe(200); + expect(response.body[0]).toHaveProperty('question', "¿Cuál es la población de Oviedo?"); +} \ No newline at end of file From 88c100db3a8c632ab320abc81a2507ea94d04dfb Mon Sep 17 00:00:00 2001 From: Mister-Mario Date: Mon, 15 Apr 2024 23:51:43 +0200 Subject: [PATCH 15/40] Removed unused hook for e2e tests --- webapp/src/components/GameConfigurator/GameConfigurator.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webapp/src/components/GameConfigurator/GameConfigurator.js b/webapp/src/components/GameConfigurator/GameConfigurator.js index 1c592294..582f6d13 100644 --- a/webapp/src/components/GameConfigurator/GameConfigurator.js +++ b/webapp/src/components/GameConfigurator/GameConfigurator.js @@ -9,7 +9,7 @@ function GameConfigurator(){ const [tipoPregunta, setTipoPregunta] = useState('POPULATION'); const [numeroPreguntas, setNumeroPreguntas] = useState(5); const [clickedForNewGame, setClickedForNewGame]= useState(false); - const[t, i18n] = useTranslation("global"); + const[t] = useTranslation("global"); function handleClick() { setClickedForNewGame(true); From 6a49ed9ad1ad51f2607ea972cc4d357e1e6c621d Mon Sep 17 00:00:00 2001 From: uo289267 Date: Tue, 16 Apr 2024 22:09:24 +0200 Subject: [PATCH 16/40] Added Ranking View need of style --- webapp/src/App.js | 2 + .../questionView/QuestionGenerator.js | 6 +- .../components/ranking/RankingRetriever.js | 83 +++++++++++++++++++ webapp/src/components/ranking/RankingView.js | 58 +++++++++++++ 4 files changed, 146 insertions(+), 3 deletions(-) create mode 100644 webapp/src/components/ranking/RankingRetriever.js create mode 100644 webapp/src/components/ranking/RankingView.js diff --git a/webapp/src/App.js b/webapp/src/App.js index 9ed9bbde..cc14d644 100644 --- a/webapp/src/App.js +++ b/webapp/src/App.js @@ -12,6 +12,7 @@ import './custom.css'; import HistoricalView from './components/HistoricalData/HistoricalView'; import { UserContextProvider } from './components/loginAndRegistration/UserContext'; import GameConfigurator from './components/GameConfigurator/GameConfigurator'; +import RankingView from './components/ranking/RankingView'; function App() { @@ -34,6 +35,7 @@ function App() { } /> } /> }/> + } />
diff --git a/webapp/src/components/questionView/QuestionGenerator.js b/webapp/src/components/questionView/QuestionGenerator.js index b2c278e3..2add8f05 100644 --- a/webapp/src/components/questionView/QuestionGenerator.js +++ b/webapp/src/components/questionView/QuestionGenerator.js @@ -9,7 +9,7 @@ class QuestionGenerator{ } async generateQuestions(lang, type, amount) { - /* + try { //const response = await fetch(this.apiUrl); //const receivedQuestions = await response.json(); @@ -34,9 +34,9 @@ class QuestionGenerator{ } catch (error) { throw new Error(error); } - */ + /* try { let response; if(type==="COMPETITIVE"){ @@ -55,7 +55,7 @@ class QuestionGenerator{ } catch (error) { throw new Error(error); } - + */ } } diff --git a/webapp/src/components/ranking/RankingRetriever.js b/webapp/src/components/ranking/RankingRetriever.js new file mode 100644 index 00000000..c988f218 --- /dev/null +++ b/webapp/src/components/ranking/RankingRetriever.js @@ -0,0 +1,83 @@ +import axios from 'axios'; + +class RankingRetriever{ + + constructor(){ + this.apiUrl = (process.env.REACT_APP_API_ENDPOINT || 'http://localhost:8000'); + + } + + async getTopTen() { + /* + try { + const response = await axios.get(this.apiUrl + '/top10');//finding the top ten + const receivedTopTenRanking = await response.data; + return receivedTopTenRanking.record; + } catch (error) { + console.log(error) + throw new Error(error); + + }*/ + console.log("getting top 10") + return { + "usersCompetitiveStats": [ + { + "_id": "user", + "totalPoints": 1000, + "totalCompetitiveGames": 4 + }, + { + "_id": "user2", + "totalPoints": 900, + "totalCompetitiveGames": 2 + }, + { + "_id": "user3", + "totalPoints": 800, + "totalCompetitiveGames": 3 + }, + { + "_id": "user4", + "totalPoints": 700, + "totalCompetitiveGames": 5 + }, + { + "_id": "user5", + "totalPoints": 600, + "totalCompetitiveGames": 6 + }, + { + "_id": "user6", + "totalPoints": 500, + "totalCompetitiveGames": 7 + }, + { + "_id": "user7", + "totalPoints": 400, + "totalCompetitiveGames": 8 + }, + { + "_id": "user8", + "totalPoints": 300, + "totalCompetitiveGames": 9 + }, + { + "_id": "user9", + "totalPoints": 200, + "totalCompetitiveGames": 10 + }, + { + "_id": "user10", + "totalPoints": 100, + "totalCompetitiveGames": 11 + } + ] + } + } + + + +} + +export default RankingRetriever; + diff --git a/webapp/src/components/ranking/RankingView.js b/webapp/src/components/ranking/RankingView.js new file mode 100644 index 00000000..f00c5696 --- /dev/null +++ b/webapp/src/components/ranking/RankingView.js @@ -0,0 +1,58 @@ +import React, { useState, useEffect } from 'react'; +import RankingRetriever from './RankingRetriever'; +const retriever = new RankingRetriever(); + +const RankingView = () => { + const [rankingData, setRankingData] = useState(null); + + const getRanking = async () => { + try { + console.log("caalling") + var ranking = await retriever.getTopTen(); + setRankingData(ranking.usersCompetitiveStats); + console.log(ranking) + console.log(rankingData) + } catch (error) { + console.log(error); + } + } + + useEffect(() => { + getRanking(); + }, []); + + return ( +
+

Ranking

+ {console.log("DIV")} + {console.log(rankingData)} + {rankingData && rankingData.length > 0 ? ( + + + + + + + + + + + + {rankingData.map((user, index) => ( + + + + + + + ))} + +
PositionUsernamePointsNum of Games
{index + 1}{user._id}{user.totalPoints}{user.totalCompetitiveGames}
+ ) : ( +

Loading...

+ )} +
+ ); +}; + +export default RankingView; From c1783678e3b8027f2fa4efccb1c14abec93965fe Mon Sep 17 00:00:00 2001 From: uo289267 Date: Wed, 17 Apr 2024 13:00:05 +0200 Subject: [PATCH 17/40] Added ranking View --- webapp/src/components/GameMenu/GameMenu.js | 10 ++ webapp/src/components/fragments/Loader.js | 19 +++ .../components/ranking/RankingRetriever.js | 18 ++- webapp/src/components/ranking/RankingView.js | 38 +++--- webapp/src/custom.css | 117 ++++++++++++++++++ webapp/src/translations/en/global.json | 8 ++ webapp/src/translations/es/global.json | 8 ++ 7 files changed, 203 insertions(+), 15 deletions(-) create mode 100644 webapp/src/components/fragments/Loader.js diff --git a/webapp/src/components/GameMenu/GameMenu.js b/webapp/src/components/GameMenu/GameMenu.js index e0e65053..5e7f140e 100644 --- a/webapp/src/components/GameMenu/GameMenu.js +++ b/webapp/src/components/GameMenu/GameMenu.js @@ -11,6 +11,7 @@ export default function GameMenu() {

{t("gameMenu.title")}

+ ); } @@ -24,4 +25,13 @@ export default function GameMenu() { ); } + function ButtonRanking({ t }) { + return ( + +

{t("gameMenu.view_ranking")}

+ + + ); + } + \ No newline at end of file diff --git a/webapp/src/components/fragments/Loader.js b/webapp/src/components/fragments/Loader.js new file mode 100644 index 00000000..ff092709 --- /dev/null +++ b/webapp/src/components/fragments/Loader.js @@ -0,0 +1,19 @@ +import React from 'react'; + +const Loader = () => { + return ( +
+
+
Status
+
+
+
+
+
+
+
Loading...
+
+ ); +} + +export default Loader; diff --git a/webapp/src/components/ranking/RankingRetriever.js b/webapp/src/components/ranking/RankingRetriever.js index c988f218..0f04736e 100644 --- a/webapp/src/components/ranking/RankingRetriever.js +++ b/webapp/src/components/ranking/RankingRetriever.js @@ -18,7 +18,6 @@ class RankingRetriever{ throw new Error(error); }*/ - console.log("getting top 10") return { "usersCompetitiveStats": [ { @@ -74,6 +73,23 @@ class RankingRetriever{ ] } } + async getMyPosition(user){ + /* + try { + const response = await axios.get(this.apiUrl + '/'+user);//finding the top ten + const receivedMyRanking = await response.data; + return receivedMyRanking.record; + } catch (error) { + console.log(error) + throw new Error(error); + + }*/ + return { + "_id": "user", + "totalPoints": 1000, + "totalCompetitiveGames": 4 + }; + } diff --git a/webapp/src/components/ranking/RankingView.js b/webapp/src/components/ranking/RankingView.js index f00c5696..c195c21c 100644 --- a/webapp/src/components/ranking/RankingView.js +++ b/webapp/src/components/ranking/RankingView.js @@ -1,17 +1,20 @@ import React, { useState, useEffect } from 'react'; import RankingRetriever from './RankingRetriever'; +import {useTranslation} from "react-i18next"; +import Loader from "../fragments/Loader" const retriever = new RankingRetriever(); const RankingView = () => { const [rankingData, setRankingData] = useState(null); - + const [myRankingData, setMyRankingData] = useState(null); + const[t] = useTranslation("global"); const getRanking = async () => { try { - console.log("caalling") var ranking = await retriever.getTopTen(); setRankingData(ranking.usersCompetitiveStats); - console.log(ranking) - console.log(rankingData) + var myrank = await retriever.getMyPosition(); + setMyRankingData(myrank); + console.log(myrank) } catch (error) { console.log(error); } @@ -22,19 +25,16 @@ const RankingView = () => { }, []); return ( -
-

Ranking

- {console.log("DIV")} - {console.log(rankingData)} +
+

{t("ranking.ranking")}

{rankingData && rankingData.length > 0 ? ( - - - - - + + + + @@ -46,10 +46,20 @@ const RankingView = () => { ))} + {/* Blank row */} + + + + + + + + +
PositionUsernamePointsNum of Games{t("ranking.position")}{t("ranking.username")}{t("ranking.points")}{t("ranking.num_games")}
{user.totalCompetitiveGames}
0{myRankingData._id}{myRankingData.totalPoints}{myRankingData.totalCompetitiveGames}
) : ( -

Loading...

+ < Loader /> )}
); diff --git a/webapp/src/custom.css b/webapp/src/custom.css index 3c30da2f..f3aa99e9 100644 --- a/webapp/src/custom.css +++ b/webapp/src/custom.css @@ -1300,6 +1300,98 @@ svg { h2{ text-align: center; } +/*------------------------Loader------------------------------*/ +@keyframes blinkCursor { + 50% { + border-right-color: transparent; + } +} + +@keyframes typeAndDelete { + 0%, + 10% { + width: 0; + } + 45%, + 55% { + width: 6.2em; + } /* adjust width based on content */ + 90%, + 100% { + width: 0; + } +} + +.terminal-loader { + border: 0.1em solid #333; + background-color: #1a1a1a; + color: #0f0; + font-family: "Courier New", Courier, monospace; + font-size: 1em; + padding: 1.5em 1em; + width: 12em; + margin: 100px auto; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); + border-radius: 4px; + position: relative; + overflow: hidden; + box-sizing: border-box; +} + +.terminal-header { + position: absolute; + top: 0; + left: 0; + right: 0; + height: 1.5em; + background-color: #333; + border-top-left-radius: 4px; + border-top-right-radius: 4px; + padding: 0 0.4em; + box-sizing: border-box; +} + +.terminal-controls { + float: right; +} + +.control { + display: inline-block; + width: 0.6em; + height: 0.6em; + margin-left: 0.4em; + border-radius: 50%; + background-color: #777; +} + +.control.close { + background-color: #e33; +} + +.control.minimize { + background-color: #ee0; +} + +.control.maximize { + background-color: #0b0; +} + +.terminal-title { + float: left; + line-height: 1.5em; + color: #eee; +} + +.text { + display: inline-block; + white-space: nowrap; + overflow: hidden; + border-right: 0.2em solid green; /* Cursor */ + animation: typeAndDelete 4s steps(11) infinite, + blinkCursor 0.5s step-end infinite alternate; + margin-top: 1.5em; +} + /*--------------------------------------------------Configurator---------------------------------*/ /* Estilo para el elemento select */ .select-style { @@ -1346,4 +1438,29 @@ h2{ margin-top:20px; margin-bottom:20px; } +.table tbody tr.penultimate-row td { + background-color: black; + padding: 25px; +} + +.table th,td{ + padding-left: 30px; + padding-right: 30px; + padding-top: 10px; + padding-bottom: 10px; + text-align: center; + +} +/* Estilo para todos los td excepto el penúltimo */ +.table tbody tr:not(:nth-last-child(2)) td:nth-child(1) { + background-color: #69276f; +} + + +.table tr:nth-child(even) {background-color: #f2f2f22a;} +.table th { + background-color: #69276f; + color: white; +} +.table tr:hover {background-color: #a59c9ca2;} diff --git a/webapp/src/translations/en/global.json b/webapp/src/translations/en/global.json index 28c2a341..2c824262 100644 --- a/webapp/src/translations/en/global.json +++ b/webapp/src/translations/en/global.json @@ -55,6 +55,7 @@ "gameMenu":{ "history_button":"View Historical Data", "new_game_button":"Create New Game", + "view_ranking":"Ranking", "title":"Game Menu", "back":"Back" }, @@ -84,6 +85,13 @@ "option_size":"Size", "custo_game":"Create custom game", "competi_game":"Play Competitive" + }, + "ranking":{ + "ranking":"Ranking", + "position":"Position", + "username":"Username", + "points":"Points", + "num_games":"Num Competitive games" } } diff --git a/webapp/src/translations/es/global.json b/webapp/src/translations/es/global.json index 32f9e22a..1692648c 100644 --- a/webapp/src/translations/es/global.json +++ b/webapp/src/translations/es/global.json @@ -59,6 +59,7 @@ "gameMenu":{ "history_button":"Ver Historial", "new_game_button":"Crear nuevo juego", + "view_ranking":"Ranking", "title":"Menú del Juego", "back":"Atrás" },"questionView":{ @@ -87,6 +88,13 @@ "option_size":"Extensión", "custo_game":"Crea una partida personalizada", "competi_game":"Juega en modo Competitivo" + }, + "ranking":{ + "ranking":"Ranking", + "position":"Posición", + "username":"Username", + "points":"Puntos", + "num_games":"Nº juegos Competitivos" } } From 99425849d4414b1df597108687685c424764b9dd Mon Sep 17 00:00:00 2001 From: uo289267 Date: Wed, 17 Apr 2024 13:05:38 +0200 Subject: [PATCH 18/40] Added back button to menu in ranking --- webapp/src/components/ranking/RankingView.js | 2 ++ webapp/src/custom.css | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/webapp/src/components/ranking/RankingView.js b/webapp/src/components/ranking/RankingView.js index c195c21c..6f58043a 100644 --- a/webapp/src/components/ranking/RankingView.js +++ b/webapp/src/components/ranking/RankingView.js @@ -2,6 +2,7 @@ import React, { useState, useEffect } from 'react'; import RankingRetriever from './RankingRetriever'; import {useTranslation} from "react-i18next"; import Loader from "../fragments/Loader" +import BackButton from '../fragments/BackButtonToGameMenu'; const retriever = new RankingRetriever(); const RankingView = () => { @@ -26,6 +27,7 @@ const RankingView = () => { return (
+

{t("ranking.ranking")}

{rankingData && rankingData.length > 0 ? ( diff --git a/webapp/src/custom.css b/webapp/src/custom.css index f3aa99e9..d0349386 100644 --- a/webapp/src/custom.css +++ b/webapp/src/custom.css @@ -1231,8 +1231,8 @@ svg { font-size: 1em; color: black; font-weight: 700; - margin-top: 3em; - margin-bottom: 3em; + margin-top: 1.5em; + margin-bottom: 1em; text-decoration: none; } @@ -1440,7 +1440,7 @@ h2{ } .table tbody tr.penultimate-row td { background-color: black; - padding: 25px; + padding: 15px; } .table th,td{ From 8c179b94f091ffc722d584ee558434caae9670db Mon Sep 17 00:00:00 2001 From: uo289267 Date: Wed, 17 Apr 2024 18:26:21 +0200 Subject: [PATCH 19/40] Added test for BackButton and RankingView (need finishing up) --- .../src/components/GameMenu/GameMenu.test.js | 6 + .../fragments/BackButtonToGameMenu.test.js | 31 ++ .../components/ranking/RankingRetriever.js | 8 +- .../components/ranking/RankingView.test.js | 336 ++++++++++++++++++ 4 files changed, 377 insertions(+), 4 deletions(-) create mode 100644 webapp/src/components/fragments/BackButtonToGameMenu.test.js create mode 100644 webapp/src/components/ranking/RankingView.test.js diff --git a/webapp/src/components/GameMenu/GameMenu.test.js b/webapp/src/components/GameMenu/GameMenu.test.js index 7d9b7e4d..a52ed153 100644 --- a/webapp/src/components/GameMenu/GameMenu.test.js +++ b/webapp/src/components/GameMenu/GameMenu.test.js @@ -33,6 +33,12 @@ describe('GameMenu component', () => { const text = screen.getByText(i18en.t('gameMenu.history_button')); expect(text).toBeInTheDocument(); }); + + it('renders option to view ranking data', () => { + render(); + const text = screen.getByText(i18en.t('gameMenu.view_ranking')); + expect(text).toBeInTheDocument(); + }); }); diff --git a/webapp/src/components/fragments/BackButtonToGameMenu.test.js b/webapp/src/components/fragments/BackButtonToGameMenu.test.js new file mode 100644 index 00000000..586cf695 --- /dev/null +++ b/webapp/src/components/fragments/BackButtonToGameMenu.test.js @@ -0,0 +1,31 @@ +import { render, screen } from '@testing-library/react'; +import BackButtonToGameMenu from './BackButtonToGameMenu'; +import { MemoryRouter } from 'react-router-dom'; + +import { initReactI18next } from 'react-i18next'; +import i18en from 'i18next'; + +i18en.use(initReactI18next).init({ + resources: {}, + lng: 'en', + interpolation:{ + escapeValue: false, + } +}); +global.i18en = i18en; + + +describe('BackButtonToGameMenu component', () => { + + it('renders option to go back to the game menu', () => { + render(); + const text = screen.getByText((content, element) => { + const regex = new RegExp(i18en.t("gameMenu.back")); + return regex.test(content); + }); + + expect(text).toBeInTheDocument(); + }); +}); + + diff --git a/webapp/src/components/ranking/RankingRetriever.js b/webapp/src/components/ranking/RankingRetriever.js index 0f04736e..b4f5b2b2 100644 --- a/webapp/src/components/ranking/RankingRetriever.js +++ b/webapp/src/components/ranking/RankingRetriever.js @@ -3,7 +3,7 @@ import axios from 'axios'; class RankingRetriever{ constructor(){ - this.apiUrl = (process.env.REACT_APP_API_ENDPOINT || 'http://localhost:8000'); + this.apiUrl = (process.env.REACT_APP_API_ENDPOINT || 'http://localhost:8000')+ "/record/ranking"; } @@ -85,9 +85,9 @@ class RankingRetriever{ }*/ return { - "_id": "user", - "totalPoints": 1000, - "totalCompetitiveGames": 4 + "_id": "myUser", + "totalPoints": 250, + "totalCompetitiveGames": 1 }; } diff --git a/webapp/src/components/ranking/RankingView.test.js b/webapp/src/components/ranking/RankingView.test.js new file mode 100644 index 00000000..93236ca9 --- /dev/null +++ b/webapp/src/components/ranking/RankingView.test.js @@ -0,0 +1,336 @@ +import { render , screen, waitFor, fireEvent } from '@testing-library/react'; +import { MemoryRouter } from 'react-router-dom'; +import axios from 'axios'; +import { initReactI18next } from 'react-i18next'; +import i18en from 'i18next'; +import RankingView from './RankingView'; +import MockAdapter from 'axios-mock-adapter'; +import { act } from 'react-dom/test-utils'; +i18en.use(initReactI18next).init({ + resources: {}, + lng: 'en', + interpolation:{ + escapeValue: false, + } +}); +global.i18en = i18en; + +const mockAxios = new MockAdapter(axios); +describe('GameMenu component', () => { + beforeEach(() => { + mockAxios.reset(); + }); + + it('renders title', () => { + act(()=>{ + render(); + }) + const text = screen.getByText(i18en.t('ranking.ranking')); + expect(text).toBeInTheDocument(); + }); + it('renders Loading if the call to the gateway has not been done', () => { + act(()=>{ + render(); + }) + const text = screen.getByText('Loading...'); + expect(text).toBeInTheDocument(); + }); + it('renders position all headers in the table',async ()=>{ + mockAxios.onGet('http://localhost:8000/ranking/top10').reply(200, + { + "usersCompetitiveStats": [ + { + "_id": "user", + "totalPoints": 1000, + "totalCompetitiveGames": 4 + }, + { + "_id": "user2", + "totalPoints": 900, + "totalCompetitiveGames": 2 + }, + { + "_id": "user3", + "totalPoints": 800, + "totalCompetitiveGames": 3 + }, + { + "_id": "user4", + "totalPoints": 700, + "totalCompetitiveGames": 5 + }, + { + "_id": "user5", + "totalPoints": 600, + "totalCompetitiveGames": 6 + }, + { + "_id": "user6", + "totalPoints": 500, + "totalCompetitiveGames": 7 + }, + { + "_id": "user7", + "totalPoints": 400, + "totalCompetitiveGames": 8 + }, + { + "_id": "user8", + "totalPoints": 300, + "totalCompetitiveGames": 9 + }, + { + "_id": "user9", + "totalPoints": 200, + "totalCompetitiveGames": 10 + }, + { + "_id": "user10", + "totalPoints": 100, + "totalCompetitiveGames": 11 + } + ] + }); + await act(async () =>{ + await render(); + + }) + await waitFor(() => expect(screen.getByText(i18en.t('ranking.position'))).toBeInTheDocument()); + expect(screen.getByText(i18en.t('ranking.username'))).toBeInTheDocument() + expect(screen.getByText(i18en.t('ranking.points'))).toBeInTheDocument() + expect(screen.getByText(i18en.t('ranking.num_games'))).toBeInTheDocument() + }); + it('renders position all users usernames',async ()=>{ + mockAxios.onGet('http://localhost:8000/ranking/top10').reply(200, + { + "usersCompetitiveStats": [ + { + "_id": "user1", + "totalPoints": 1000, + "totalCompetitiveGames": 4 + }, + { + "_id": "user2", + "totalPoints": 900, + "totalCompetitiveGames": 2 + }, + { + "_id": "user3", + "totalPoints": 800, + "totalCompetitiveGames": 3 + }, + { + "_id": "user4", + "totalPoints": 700, + "totalCompetitiveGames": 5 + }, + { + "_id": "user5", + "totalPoints": 600, + "totalCompetitiveGames": 6 + }, + { + "_id": "user6", + "totalPoints": 500, + "totalCompetitiveGames": 7 + }, + { + "_id": "user7", + "totalPoints": 400, + "totalCompetitiveGames": 8 + }, + { + "_id": "user8", + "totalPoints": 300, + "totalCompetitiveGames": 9 + }, + { + "_id": "user9", + "totalPoints": 200, + "totalCompetitiveGames": 10 + }, + { + "_id": "user10", + "totalPoints": 100, + "totalCompetitiveGames": 11 + } + ] + }); + await act(async () =>{ + await render(); + + }) + await waitFor(() => expect(screen.getByText(i18en.t('ranking.position'))).toBeInTheDocument()); + //expect(screen.getByText("user1")).toBeInTheDocument() + expect(screen.getByText("user2")).toBeInTheDocument() + expect(screen.getByText("user3")).toBeInTheDocument() + expect(screen.getByText("user4")).toBeInTheDocument() + expect(screen.getByText("user5")).toBeInTheDocument() + expect(screen.getByText("user6")).toBeInTheDocument() + expect(screen.getByText("user7")).toBeInTheDocument() + expect(screen.getByText("user8")).toBeInTheDocument() + expect(screen.getByText("user9")).toBeInTheDocument() + expect(screen.getByText("user10")).toBeInTheDocument() + }); + it('renders position all users totalPoints',async ()=>{ + mockAxios.onGet('http://localhost:8000/ranking/top10').reply(200, + { + "usersCompetitiveStats": [ + { + "_id": "user1", + "totalPoints": 1000, + "totalCompetitiveGames": 4 + }, + { + "_id": "user2", + "totalPoints": 900, + "totalCompetitiveGames": 2 + }, + { + "_id": "user3", + "totalPoints": 800, + "totalCompetitiveGames": 3 + }, + { + "_id": "user4", + "totalPoints": 700, + "totalCompetitiveGames": 5 + }, + { + "_id": "user5", + "totalPoints": 600, + "totalCompetitiveGames": 6 + }, + { + "_id": "user6", + "totalPoints": 500, + "totalCompetitiveGames": 7 + }, + { + "_id": "user7", + "totalPoints": 400, + "totalCompetitiveGames": 8 + }, + { + "_id": "user8", + "totalPoints": 300, + "totalCompetitiveGames": 9 + }, + { + "_id": "user9", + "totalPoints": 200, + "totalCompetitiveGames": 10 + }, + { + "_id": "user10", + "totalPoints": 100, + "totalCompetitiveGames": 11 + } + ] + }); + await act(async () =>{ + await render(); + + }) + await waitFor(() => expect(screen.getByText(i18en.t('ranking.position'))).toBeInTheDocument()); + expect(screen.getByText("1000")).toBeInTheDocument() + expect(screen.getByText("900")).toBeInTheDocument() + expect(screen.getByText("800")).toBeInTheDocument() + expect(screen.getByText("700")).toBeInTheDocument() + expect(screen.getByText("600")).toBeInTheDocument() + expect(screen.getByText("500")).toBeInTheDocument() + expect(screen.getByText("400")).toBeInTheDocument() + expect(screen.getByText("300")).toBeInTheDocument() + expect(screen.getByText("200")).toBeInTheDocument() +}); +it('renders position all users competitive games',async ()=>{ + mockAxios.onGet('http://localhost:8000/ranking/top10').reply(200, + { + "usersCompetitiveStats": [ + { + "_id": "user1", + "totalPoints": 1000, + "totalCompetitiveGames": 4 + }, + { + "_id": "user2", + "totalPoints": 900, + "totalCompetitiveGames": 2 + }, + { + "_id": "user3", + "totalPoints": 800, + "totalCompetitiveGames": 3 + }, + { + "_id": "user4", + "totalPoints": 700, + "totalCompetitiveGames": 5 + }, + { + "_id": "user5", + "totalPoints": 600, + "totalCompetitiveGames": 6 + }, + { + "_id": "user6", + "totalPoints": 500, + "totalCompetitiveGames": 7 + }, + { + "_id": "user7", + "totalPoints": 400, + "totalCompetitiveGames": 8 + }, + { + "_id": "user8", + "totalPoints": 300, + "totalCompetitiveGames": 9 + }, + { + "_id": "user9", + "totalPoints": 200, + "totalCompetitiveGames": 10 + }, + { + "_id": "user10", + "totalPoints": 100, + "totalCompetitiveGames": 11 + } + ] + }); + await act(async () =>{ + await render(); + + }) + await waitFor(() => expect(screen.getByText(i18en.t('ranking.position'))).toBeInTheDocument()); + /* + expect(screen.getByText("2")).toBeInTheDocument() + expect(screen.getByText("5")).toBeInTheDocument() + expect(screen.getByText("6")).toBeInTheDocument() + expect(screen.getByText("7")).toBeInTheDocument()*/ +}); +it('renders position all users competitive games',async ()=>{ + mockAxios.onGet('http://localhost:8000/ranking/user').reply(200, + { + "_id": "myUser", + "totalPoints": 250, + "totalCompetitiveGames": 1 + } + ); + await act(async () =>{ + await render(); + + }) + await waitFor(() => expect(screen.getByText(i18en.t('ranking.position'))).toBeInTheDocument()); + + expect(screen.getByText("myUser")).toBeInTheDocument() + expect(screen.getByText("250")).toBeInTheDocument() + //should be one if only your rank is shown + expect(screen.getAllByText(/1/).length).toBeGreaterThanOrEqual(2);//hay dos pq hay una posicion + +}); + +}); + + From ee00ba2520aa4588c47595a0f7d9b2e4ac02c190 Mon Sep 17 00:00:00 2001 From: uo289267 Date: Wed, 17 Apr 2024 18:43:14 +0200 Subject: [PATCH 20/40] Added Game Configurator tests --- .../GameConfigurator/GameConfigurator.js | 8 +-- .../GameConfigurator/GameConfigurator.test.js | 51 +++++++++++++++++++ 2 files changed, 55 insertions(+), 4 deletions(-) create mode 100644 webapp/src/components/GameConfigurator/GameConfigurator.test.js diff --git a/webapp/src/components/GameConfigurator/GameConfigurator.js b/webapp/src/components/GameConfigurator/GameConfigurator.js index 582f6d13..338e97ce 100644 --- a/webapp/src/components/GameConfigurator/GameConfigurator.js +++ b/webapp/src/components/GameConfigurator/GameConfigurator.js @@ -20,8 +20,8 @@ function GameConfigurator(){

{t("gameConfigurator.game_config")}

{t("gameConfigurator.custo_game")}

- - setTipoPregunta(e.target.value)}> @@ -29,9 +29,9 @@ function GameConfigurator(){

- + {/* Spinner para seleccionar el número de preguntas */} - setNumeroPreguntas(e.target.value)} diff --git a/webapp/src/components/GameConfigurator/GameConfigurator.test.js b/webapp/src/components/GameConfigurator/GameConfigurator.test.js new file mode 100644 index 00000000..273b93c0 --- /dev/null +++ b/webapp/src/components/GameConfigurator/GameConfigurator.test.js @@ -0,0 +1,51 @@ +import React from 'react'; +import { render, screen, fireEvent } from '@testing-library/react'; +import '@testing-library/jest-dom/extend-expect'; // For additional matchers like toBeInTheDocument +import { BrowserRouter as Router } from 'react-router-dom'; +import GameConfigurator from './GameConfigurator'; +import { initReactI18next } from 'react-i18next'; +import i18en from 'i18next'; + + +i18en.use(initReactI18next).init({ + resources: {}, + lng: 'en', + interpolation:{ + escapeValue: false, + } +}); +global.i18en = i18en; + +describe('GameConfigurator', () => { + test('renders GameConfigurator component', () => { + render(); + expect(screen.getByText(i18en.t("gameConfigurator.game_config"))).toBeInTheDocument(); + }); + + + test('updates tipoPregunta state when select value changes', () => { + render(); + const selectElement = screen.getByLabelText(i18en.t("gameConfigurator.type_quest")); + fireEvent.change(selectElement, { target: { value: 'CAPITAL' } }); + expect(selectElement.value).toBe('CAPITAL'); + }); + + test('updates numeroPreguntas state when input value changes', () => { + render(); + const inputElement = screen.getByLabelText(i18en.t("gameConfigurator.num_quest")); + fireEvent.change(inputElement, { target: { value: '10' } }); + expect(inputElement.value).toBe('10'); + }); + it('renders option to play customized game', () => { + render(); + const text = screen.getByText(i18en.t('gameConfigurator.custo_game')); + expect(text).toBeInTheDocument(); +}); + +it('renders option to play Competitive game', () => { + render(); + const text = screen.getByText(i18en.t('gameConfigurator.competi_game')); + expect(text).toBeInTheDocument(); +}); + +}); From 7a715c0b5b73b5efad750f4824b2b129b91643e2 Mon Sep 17 00:00:00 2001 From: uo289267 Date: Wed, 17 Apr 2024 21:57:31 +0200 Subject: [PATCH 21/40] Finished Ranking jest tests --- .../GameConfigurator/GameConfigurator.js | 17 + webapp/src/components/GameMenu/GameMenu.js | 2 + webapp/src/components/fragments/Loader.js | 6 +- .../components/ranking/RankingRetriever.js | 20 +- webapp/src/components/ranking/RankingView.js | 14 +- .../components/ranking/RankingView.test.js | 307 +++++------------- webapp/src/custom.css | 52 +-- webapp/src/translations/en/global.json | 4 +- webapp/src/translations/es/global.json | 4 +- 9 files changed, 150 insertions(+), 276 deletions(-) diff --git a/webapp/src/components/GameConfigurator/GameConfigurator.js b/webapp/src/components/GameConfigurator/GameConfigurator.js index 338e97ce..4a536729 100644 --- a/webapp/src/components/GameConfigurator/GameConfigurator.js +++ b/webapp/src/components/GameConfigurator/GameConfigurator.js @@ -14,14 +14,25 @@ function GameConfigurator(){ function handleClick() { setClickedForNewGame(true); } + + function handleClickRandomize() { + const options = ['ALL', 'POPULATION', 'CAPITAL', 'LANGUAGE', 'SIZE']; + const randomOptionIndex = Math.floor(Math.random() * options.length); + setTipoPregunta(options[randomOptionIndex]); + + const randomNumQuestions = Math.floor(Math.random() * 20) + 1; // Random number between 1 and 20 + setNumeroPreguntas(randomNumQuestions); + } return ( clickedForNewGame ? :

{t("gameConfigurator.game_config")}

{t("gameConfigurator.custo_game")}

+
diff --git a/webapp/src/components/ranking/RankingView.test.js b/webapp/src/components/ranking/RankingView.test.js index 93236ca9..c9e44535 100644 --- a/webapp/src/components/ranking/RankingView.test.js +++ b/webapp/src/components/ranking/RankingView.test.js @@ -6,6 +6,7 @@ import i18en from 'i18next'; import RankingView from './RankingView'; import MockAdapter from 'axios-mock-adapter'; import { act } from 'react-dom/test-utils'; +import { UserContextProvider} from '../loginAndRegistration/UserContext'; i18en.use(initReactI18next).init({ resources: {}, lng: 'en', @@ -16,96 +17,31 @@ i18en.use(initReactI18next).init({ global.i18en = i18en; const mockAxios = new MockAdapter(axios); -describe('GameMenu component', () => { - beforeEach(() => { - mockAxios.reset(); - }); +describe('RankingView component', () => { + it('renders title', () => { act(()=>{ - render(); + render(); }) const text = screen.getByText(i18en.t('ranking.ranking')); expect(text).toBeInTheDocument(); }); it('renders Loading if the call to the gateway has not been done', () => { act(()=>{ - render(); + render(); }) const text = screen.getByText('Loading...'); expect(text).toBeInTheDocument(); }); - it('renders position all headers in the table',async ()=>{ - mockAxios.onGet('http://localhost:8000/ranking/top10').reply(200, - { - "usersCompetitiveStats": [ - { - "_id": "user", - "totalPoints": 1000, - "totalCompetitiveGames": 4 - }, - { - "_id": "user2", - "totalPoints": 900, - "totalCompetitiveGames": 2 - }, - { - "_id": "user3", - "totalPoints": 800, - "totalCompetitiveGames": 3 - }, - { - "_id": "user4", - "totalPoints": 700, - "totalCompetitiveGames": 5 - }, - { - "_id": "user5", - "totalPoints": 600, - "totalCompetitiveGames": 6 - }, - { - "_id": "user6", - "totalPoints": 500, - "totalCompetitiveGames": 7 - }, - { - "_id": "user7", - "totalPoints": 400, - "totalCompetitiveGames": 8 - }, - { - "_id": "user8", - "totalPoints": 300, - "totalCompetitiveGames": 9 - }, - { - "_id": "user9", - "totalPoints": 200, - "totalCompetitiveGames": 10 - }, - { - "_id": "user10", - "totalPoints": 100, - "totalCompetitiveGames": 11 - } - ] - }); - await act(async () =>{ - await render(); - - }) - await waitFor(() => expect(screen.getByText(i18en.t('ranking.position'))).toBeInTheDocument()); - expect(screen.getByText(i18en.t('ranking.username'))).toBeInTheDocument() - expect(screen.getByText(i18en.t('ranking.points'))).toBeInTheDocument() - expect(screen.getByText(i18en.t('ranking.num_games'))).toBeInTheDocument() - }); - it('renders position all users usernames',async ()=>{ - mockAxios.onGet('http://localhost:8000/ranking/top10').reply(200, +}); + + describe('RankingView component with endpoint', ()=>{ + mockAxios.onGet('http://localhost:8000/record/ranking/top10').reply(200, { "usersCompetitiveStats": [ { - "_id": "user1", + "_id": "user", "totalPoints": 1000, "totalCompetitiveGames": 4 }, @@ -155,182 +91,87 @@ describe('GameMenu component', () => { "totalCompetitiveGames": 11 } ] - }); + }); + + mockAxios.onGet('http://localhost:8000/record/ranking/user').reply(200, + { + "_id": "myUser", + "totalPoints": 250, + "totalCompetitiveGames": 1 + } + ); + + it('renders position all headers in the table',async ()=>{ + await act(async () =>{ - await render(); + await render(); }) await waitFor(() => expect(screen.getByText(i18en.t('ranking.position'))).toBeInTheDocument()); - //expect(screen.getByText("user1")).toBeInTheDocument() - expect(screen.getByText("user2")).toBeInTheDocument() - expect(screen.getByText("user3")).toBeInTheDocument() - expect(screen.getByText("user4")).toBeInTheDocument() - expect(screen.getByText("user5")).toBeInTheDocument() - expect(screen.getByText("user6")).toBeInTheDocument() - expect(screen.getByText("user7")).toBeInTheDocument() - expect(screen.getByText("user8")).toBeInTheDocument() - expect(screen.getByText("user9")).toBeInTheDocument() - expect(screen.getByText("user10")).toBeInTheDocument() + expect(screen.getByText(i18en.t('ranking.username'))).toBeInTheDocument() + expect(screen.getByText(i18en.t('ranking.points'))).toBeInTheDocument() + expect(screen.getByText(i18en.t('ranking.num_games'))).toBeInTheDocument() }); - it('renders position all users totalPoints',async ()=>{ - mockAxios.onGet('http://localhost:8000/ranking/top10').reply(200, - { - "usersCompetitiveStats": [ - { - "_id": "user1", - "totalPoints": 1000, - "totalCompetitiveGames": 4 - }, - { - "_id": "user2", - "totalPoints": 900, - "totalCompetitiveGames": 2 - }, - { - "_id": "user3", - "totalPoints": 800, - "totalCompetitiveGames": 3 - }, - { - "_id": "user4", - "totalPoints": 700, - "totalCompetitiveGames": 5 - }, - { - "_id": "user5", - "totalPoints": 600, - "totalCompetitiveGames": 6 - }, - { - "_id": "user6", - "totalPoints": 500, - "totalCompetitiveGames": 7 - }, - { - "_id": "user7", - "totalPoints": 400, - "totalCompetitiveGames": 8 - }, - { - "_id": "user8", - "totalPoints": 300, - "totalCompetitiveGames": 9 - }, - { - "_id": "user9", - "totalPoints": 200, - "totalCompetitiveGames": 10 - }, - { - "_id": "user10", - "totalPoints": 100, - "totalCompetitiveGames": 11 - } - ] - }); + it('renders position all users usernames',async ()=>{ await act(async () =>{ - await render(); + await render(); }) await waitFor(() => expect(screen.getByText(i18en.t('ranking.position'))).toBeInTheDocument()); - expect(screen.getByText("1000")).toBeInTheDocument() - expect(screen.getByText("900")).toBeInTheDocument() - expect(screen.getByText("800")).toBeInTheDocument() - expect(screen.getByText("700")).toBeInTheDocument() - expect(screen.getByText("600")).toBeInTheDocument() - expect(screen.getByText("500")).toBeInTheDocument() - expect(screen.getByText("400")).toBeInTheDocument() - expect(screen.getByText("300")).toBeInTheDocument() - expect(screen.getByText("200")).toBeInTheDocument() -}); -it('renders position all users competitive games',async ()=>{ - mockAxios.onGet('http://localhost:8000/ranking/top10').reply(200, - { - "usersCompetitiveStats": [ - { - "_id": "user1", - "totalPoints": 1000, - "totalCompetitiveGames": 4 - }, - { - "_id": "user2", - "totalPoints": 900, - "totalCompetitiveGames": 2 - }, - { - "_id": "user3", - "totalPoints": 800, - "totalCompetitiveGames": 3 - }, - { - "_id": "user4", - "totalPoints": 700, - "totalCompetitiveGames": 5 - }, - { - "_id": "user5", - "totalPoints": 600, - "totalCompetitiveGames": 6 - }, - { - "_id": "user6", - "totalPoints": 500, - "totalCompetitiveGames": 7 - }, - { - "_id": "user7", - "totalPoints": 400, - "totalCompetitiveGames": 8 - }, - { - "_id": "user8", - "totalPoints": 300, - "totalCompetitiveGames": 9 - }, - { - "_id": "user9", - "totalPoints": 200, - "totalCompetitiveGames": 10 - }, - { - "_id": "user10", - "totalPoints": 100, - "totalCompetitiveGames": 11 - } - ] - }); + //expect(screen.getByText("user1")).toBeInTheDocument() + expect(screen.getByText("user2")).toBeInTheDocument() + expect(screen.getByText("user3")).toBeInTheDocument() + expect(screen.getByText("user4")).toBeInTheDocument() + expect(screen.getByText("user5")).toBeInTheDocument() + expect(screen.getByText("user6")).toBeInTheDocument() + expect(screen.getByText("user7")).toBeInTheDocument() + expect(screen.getByText("user8")).toBeInTheDocument() + expect(screen.getByText("user9")).toBeInTheDocument() + expect(screen.getByText("user10")).toBeInTheDocument() + }); + it('renders position all users totalPoints',async ()=>{ + await act(async () =>{ - await render(); + await render(); }) await waitFor(() => expect(screen.getByText(i18en.t('ranking.position'))).toBeInTheDocument()); - /* - expect(screen.getByText("2")).toBeInTheDocument() - expect(screen.getByText("5")).toBeInTheDocument() - expect(screen.getByText("6")).toBeInTheDocument() - expect(screen.getByText("7")).toBeInTheDocument()*/ -}); -it('renders position all users competitive games',async ()=>{ - mockAxios.onGet('http://localhost:8000/ranking/user').reply(200, - { - "_id": "myUser", - "totalPoints": 250, - "totalCompetitiveGames": 1 - } - ); + expect(screen.getByText("1000")).toBeInTheDocument() + expect(screen.getByText("900")).toBeInTheDocument() + expect(screen.getByText("800")).toBeInTheDocument() + expect(screen.getByText("700")).toBeInTheDocument() + expect(screen.getByText("600")).toBeInTheDocument() + expect(screen.getByText("500")).toBeInTheDocument() + expect(screen.getByText("400")).toBeInTheDocument() + expect(screen.getByText("300")).toBeInTheDocument() + expect(screen.getByText("200")).toBeInTheDocument() + }); + it('renders position all users competitive games',async ()=>{ + await act(async () =>{ - await render(); - + await render(); + }) await waitFor(() => expect(screen.getByText(i18en.t('ranking.position'))).toBeInTheDocument()); - expect(screen.getByText("myUser")).toBeInTheDocument() - expect(screen.getByText("250")).toBeInTheDocument() - //should be one if only your rank is shown - expect(screen.getAllByText(/1/).length).toBeGreaterThanOrEqual(2);//hay dos pq hay una posicion - -}); + expect(screen.getAllByText(/2/).length).toBeGreaterThanOrEqual(2);//hay dos pq hay una posicion + expect(screen.getAllByText(/5/).length).toBeGreaterThanOrEqual(2);//hay dos pq hay una posicion + expect(screen.getAllByText(/6/).length).toBeGreaterThanOrEqual(2);//hay dos pq hay una posicion + expect(screen.getAllByText(/7/).length).toBeGreaterThanOrEqual(2);//hay dos pq hay una posicion + }); + it('renders position all users competitive games',async ()=>{ + + await act(async () =>{ + await render(); + + }) + await waitFor(() => expect(screen.getByText(i18en.t('ranking.position'))).toBeInTheDocument()); -}); + expect(screen.getByText("myUser")).toBeInTheDocument() + expect(screen.getByText("250")).toBeInTheDocument() + //should be one if only your rank is shown + expect(screen.getAllByText(/1/).length).toBeGreaterThanOrEqual(2);//hay dos pq hay una posicion + }); +}) diff --git a/webapp/src/custom.css b/webapp/src/custom.css index d0349386..836d49af 100644 --- a/webapp/src/custom.css +++ b/webapp/src/custom.css @@ -1351,30 +1351,7 @@ h2{ box-sizing: border-box; } -.terminal-controls { - float: right; -} - -.control { - display: inline-block; - width: 0.6em; - height: 0.6em; - margin-left: 0.4em; - border-radius: 50%; - background-color: #777; -} - -.control.close { - background-color: #e33; -} - -.control.minimize { - background-color: #ee0; -} -.control.maximize { - background-color: #0b0; -} .terminal-title { float: left; @@ -1394,6 +1371,25 @@ h2{ /*--------------------------------------------------Configurator---------------------------------*/ /* Estilo para el elemento select */ +.buttonRandomize{ + display: flex; + justify-content: center; + align-items: center; + width: 15em; + height: 45px; + background:#00b8ff; + border: none; + outline: none; + border-radius: 40px; + box-shadow: 0 0 10px black; + cursor: pointer; + font-size: 1em; + color: black; + font-weight: 700; + margin-top: 1.5em; + margin-bottom: 1em; + text-decoration: none; +} .select-style { width: 200px; height: 35px; @@ -1427,6 +1423,16 @@ h2{ left: 50%; top: 50%; } +/* +.GameConfiguratorDiv { + display: flex; + flex-direction: column; +} + +.GameConfiguratorDiv > *:not(:first-child):not(:nth-child(4)) { + text-align: left; +}*/ + .hr-style { border: 0; border-top: 1px solid #8e888885; /* Color gris */ diff --git a/webapp/src/translations/en/global.json b/webapp/src/translations/en/global.json index 2c824262..2afde28c 100644 --- a/webapp/src/translations/en/global.json +++ b/webapp/src/translations/en/global.json @@ -79,12 +79,14 @@ "play_custom":"Play Customized Game", "rules_competi":"Play with all kinds of questions and a quantity of 5", "play_competi":"Play competitive Game", + "option_all":"All", "option_population":"Population", "option_capital":"Capital", "option_language":"Language", "option_size":"Size", "custo_game":"Create custom game", - "competi_game":"Play Competitive" + "competi_game":"Play Competitive", + "randomize":"Randomize Parameters" }, "ranking":{ "ranking":"Ranking", diff --git a/webapp/src/translations/es/global.json b/webapp/src/translations/es/global.json index 1692648c..9f888395 100644 --- a/webapp/src/translations/es/global.json +++ b/webapp/src/translations/es/global.json @@ -82,12 +82,14 @@ "play_custom":"Jugar personalizado", "rules_competi":"Jugar con todo tipo de preguntas siendo estas 5", "play_competi":"Jugar Competitivo", + "option_all":"Todas", "option_population":"Población", "option_capital":"Capital", "option_language":"Lenguaje", "option_size":"Extensión", "custo_game":"Crea una partida personalizada", - "competi_game":"Juega en modo Competitivo" + "competi_game":"Juega en modo Competitivo", + "randomize":"Randomiza Parámetros" }, "ranking":{ "ranking":"Ranking", From c51a18d9949da70bcbb4b5577fce25f024adf218 Mon Sep 17 00:00:00 2001 From: Mister-Mario Date: Thu, 18 Apr 2024 09:44:55 +0200 Subject: [PATCH 22/40] Modified sonar cloud properties to not check code duplication on tests files --- sonar-project.properties | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sonar-project.properties b/sonar-project.properties index 6db6137a..fad83281 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -14,4 +14,5 @@ sonar.coverage.exclusions=**/*.test.js sonar.sources=webapp/src/components,users/authservice,users/userservice,gatewayservice sonar.sourceEncoding=UTF-8 sonar.exclusions=node_modules/** -sonar.javascript.lcov.reportPaths=**/coverage/lcov.info \ No newline at end of file +sonar.javascript.lcov.reportPaths=**/coverage/lcov.info +sonar.cpd.exclusions=**/*.test.js,**/*steps.js,**/*Tests.java From 4c54efca1e1e90da7fe5f78cc721aa1d14c063a5 Mon Sep 17 00:00:00 2001 From: uo289267 Date: Thu, 18 Apr 2024 11:48:56 +0200 Subject: [PATCH 23/40] Resolved jest tests problems --- webapp/src/components/questionView/QuestionGenerator.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/webapp/src/components/questionView/QuestionGenerator.js b/webapp/src/components/questionView/QuestionGenerator.js index 2add8f05..9b5a38b9 100644 --- a/webapp/src/components/questionView/QuestionGenerator.js +++ b/webapp/src/components/questionView/QuestionGenerator.js @@ -9,7 +9,7 @@ class QuestionGenerator{ } async generateQuestions(lang, type, amount) { - + /* try { //const response = await fetch(this.apiUrl); //const receivedQuestions = await response.json(); @@ -35,8 +35,8 @@ class QuestionGenerator{ throw new Error(error); } + */ - /* try { let response; if(type==="COMPETITIVE"){ @@ -55,7 +55,7 @@ class QuestionGenerator{ } catch (error) { throw new Error(error); } - */ + } } From 70a03a19f0ac084aeab9a0b92e9ce490ccc5a0a3 Mon Sep 17 00:00:00 2001 From: uo289267 Date: Thu, 18 Apr 2024 12:03:05 +0200 Subject: [PATCH 24/40] Added user for userContext in tests --- webapp/src/components/ranking/RankingView.js | 2 +- webapp/src/components/ranking/RankingView.test.js | 15 ++++++++------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/webapp/src/components/ranking/RankingView.js b/webapp/src/components/ranking/RankingView.js index 33a239fb..6d172024 100644 --- a/webapp/src/components/ranking/RankingView.js +++ b/webapp/src/components/ranking/RankingView.js @@ -17,7 +17,7 @@ const RankingView = () => { try { var ranking = await retriever.getTopTen(); setRankingData(ranking.usersCompetitiveStats); - var myrank = await retriever.getMyPosition("user"); + var myrank = await retriever.getMyPosition(user.username); setMyRankingData(myrank); console.log(myrank) } catch (error) { diff --git a/webapp/src/components/ranking/RankingView.test.js b/webapp/src/components/ranking/RankingView.test.js index c9e44535..dd0c05fd 100644 --- a/webapp/src/components/ranking/RankingView.test.js +++ b/webapp/src/components/ranking/RankingView.test.js @@ -22,7 +22,7 @@ describe('RankingView component', () => { it('renders title', () => { act(()=>{ - render(); + render(); }) const text = screen.getByText(i18en.t('ranking.ranking')); expect(text).toBeInTheDocument(); @@ -93,18 +93,19 @@ describe('RankingView component', () => { ] }); - mockAxios.onGet('http://localhost:8000/record/ranking/user').reply(200, + mockAxios.onGet('http://localhost:8000/record/ranking/myUser').reply(200, { "_id": "myUser", "totalPoints": 250, "totalCompetitiveGames": 1 } ); + const user = { username: 'myUser' }; it('renders position all headers in the table',async ()=>{ await act(async () =>{ - await render(); + await render(); }) await waitFor(() => expect(screen.getByText(i18en.t('ranking.position'))).toBeInTheDocument()); @@ -114,7 +115,7 @@ describe('RankingView component', () => { }); it('renders position all users usernames',async ()=>{ await act(async () =>{ - await render(); + await render(); }) await waitFor(() => expect(screen.getByText(i18en.t('ranking.position'))).toBeInTheDocument()); @@ -132,7 +133,7 @@ describe('RankingView component', () => { it('renders position all users totalPoints',async ()=>{ await act(async () =>{ - await render(); + await render(); }) await waitFor(() => expect(screen.getByText(i18en.t('ranking.position'))).toBeInTheDocument()); @@ -149,7 +150,7 @@ describe('RankingView component', () => { it('renders position all users competitive games',async ()=>{ await act(async () =>{ - await render(); + await render(); }) await waitFor(() => expect(screen.getByText(i18en.t('ranking.position'))).toBeInTheDocument()); @@ -162,7 +163,7 @@ describe('RankingView component', () => { it('renders position all users competitive games',async ()=>{ await act(async () =>{ - await render(); + await render(); }) await waitFor(() => expect(screen.getByText(i18en.t('ranking.position'))).toBeInTheDocument()); From 50b07810096b25eb9770982fcede6895d82ae79f Mon Sep 17 00:00:00 2001 From: uo289267 Date: Thu, 18 Apr 2024 12:57:39 +0200 Subject: [PATCH 25/40] Added new style for ranking --- .../components/ranking/RankingRetriever.js | 16 ++++---- webapp/src/components/ranking/RankingView.js | 4 +- webapp/src/custom.css | 41 ++++++++++++++++++- webapp/src/translations/en/global.json | 2 +- webapp/src/translations/es/global.json | 2 +- 5 files changed, 52 insertions(+), 13 deletions(-) diff --git a/webapp/src/components/ranking/RankingRetriever.js b/webapp/src/components/ranking/RankingRetriever.js index c455854a..d2c11001 100644 --- a/webapp/src/components/ranking/RankingRetriever.js +++ b/webapp/src/components/ranking/RankingRetriever.js @@ -8,7 +8,7 @@ class RankingRetriever{ } async getTopTen() { - + /* try { const response = await axios.get(this.apiUrl + '/top10');//finding the top ten const receivedTopTenRanking = await response.data; @@ -18,8 +18,8 @@ class RankingRetriever{ console.log(error) throw new Error(error); - } - /* + }*/ + return { "usersCompetitiveStats": [ { @@ -73,10 +73,10 @@ class RankingRetriever{ "totalCompetitiveGames": 11 } ] - }*/ + } } async getMyPosition(user){ - + /* try { const response = await axios.get(this.apiUrl + '/'+user);//finding the top ten const receivedMyRanking = await response.data; @@ -86,13 +86,13 @@ class RankingRetriever{ console.log(error) throw new Error(error); - } - /* + }*/ + return { "_id": "myUser", "totalPoints": 250, "totalCompetitiveGames": 1 - };*/ + }; } diff --git a/webapp/src/components/ranking/RankingView.js b/webapp/src/components/ranking/RankingView.js index 6d172024..2737ccf1 100644 --- a/webapp/src/components/ranking/RankingView.js +++ b/webapp/src/components/ranking/RankingView.js @@ -17,7 +17,7 @@ const RankingView = () => { try { var ranking = await retriever.getTopTen(); setRankingData(ranking.usersCompetitiveStats); - var myrank = await retriever.getMyPosition(user.username); + var myrank = await retriever.getMyPosition("user.username"); setMyRankingData(myrank); console.log(myrank) } catch (error) { @@ -32,7 +32,7 @@ const RankingView = () => { return (
-

{t("ranking.ranking")}

+

{t("ranking.ranking")}🏅

{rankingData && rankingData.length > 0 && myRankingData ? (
diff --git a/webapp/src/custom.css b/webapp/src/custom.css index 836d49af..dab0a61c 100644 --- a/webapp/src/custom.css +++ b/webapp/src/custom.css @@ -1447,6 +1447,9 @@ h2{ .table tbody tr.penultimate-row td { background-color: black; padding: 15px; + /* + border: 1px solid rgba(205, 187, 187, 0.455); + box-shadow: 0 0 10px rgba(197, 191, 191, 0.726);*/ } .table th,td{ @@ -1459,7 +1462,7 @@ h2{ } /* Estilo para todos los td excepto el penúltimo */ .table tbody tr:not(:nth-last-child(2)) td:nth-child(1) { - background-color: #69276f; + background-color: #4d1e51a8; } @@ -1470,3 +1473,39 @@ h2{ } .table tr:hover {background-color: #a59c9ca2;} +.table table{ + border-collapse: collapse; +} +/* Estilo para la fila de headers */ +.table th { + border-top: 2px solid rgba(205, 187, 187, 0.455); /* Borde superior para todas las celdas de la fila de headers */ + border-left: 2px solid rgba(205, 187, 187, 0.455); /* Borde izquierdo en la primera celda */ + border-right: 2px solid rgba(205, 187, 187, 0.455); /* Borde derecho en la última celda */ +} + +/* Estilo para las filas de datos (excepto la penúltima) */ +.table tbody tr:not(:nth-last-child(2)) td { + border-left: 2px solid rgba(205, 187, 187, 0.455); /* Borde izquierdo en la primera celda */ + border-right: 2px solid rgba(205, 187, 187, 0.455); /* Borde derecho en la última celda */ +} + +/* Borde superior e inferior para la penúltima fila */ +.table tbody tr:nth-last-child(2) td { + border-top: 2px solid rgba(205, 187, 187, 0.455); /* Borde superior */ + border-bottom: 2px solid rgba(205, 187, 187, 0.455); /* Borde inferior */ +} + +/* Borde inferior para todas las celdas de la última fila */ +.table tbody tr:last-child td { + border-bottom: 2px solid rgba(205, 187, 187, 0.455); /* Borde inferior */ +} + +/* Borde izquierdo para la primera celda de la última fila */ +.table tbody tr:last-child td:first-child { + border-left: 2px solid rgba(205, 187, 187, 0.455); /* Borde izquierdo */ +} + +/* Borde derecho para la última celda de la última fila */ +.table tbody tr:last-child td:last-child { + border-right: 2px solid rgba(205, 187, 187, 0.455); /* Borde derecho */ +} diff --git a/webapp/src/translations/en/global.json b/webapp/src/translations/en/global.json index 2afde28c..a952ad7e 100644 --- a/webapp/src/translations/en/global.json +++ b/webapp/src/translations/en/global.json @@ -93,7 +93,7 @@ "position":"Position", "username":"Username", "points":"Points", - "num_games":"Num Competitive games" + "num_games":"Competitive games" } } diff --git a/webapp/src/translations/es/global.json b/webapp/src/translations/es/global.json index 9f888395..a0888aff 100644 --- a/webapp/src/translations/es/global.json +++ b/webapp/src/translations/es/global.json @@ -96,7 +96,7 @@ "position":"Posición", "username":"Username", "points":"Puntos", - "num_games":"Nº juegos Competitivos" + "num_games":"Juegos Competitivos" } } From 8c0b47f31d9d86525295c7a7c257506d19b75571 Mon Sep 17 00:00:00 2001 From: uo289267 Date: Thu, 18 Apr 2024 13:24:19 +0200 Subject: [PATCH 26/40] Added gameMenu tests to e2e --- webapp/e2e/features/gameMenu.feature | 4 ++++ webapp/e2e/steps/gameMenu.steps.js | 36 ++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 webapp/e2e/features/gameMenu.feature create mode 100644 webapp/e2e/steps/gameMenu.steps.js diff --git a/webapp/e2e/features/gameMenu.feature b/webapp/e2e/features/gameMenu.feature new file mode 100644 index 00000000..3953d899 --- /dev/null +++ b/webapp/e2e/features/gameMenu.feature @@ -0,0 +1,4 @@ +Feature: Game Menu page functionality + Scenario: There should be visible three links + Given I am on the game menu + Then three buttons should be visible diff --git a/webapp/e2e/steps/gameMenu.steps.js b/webapp/e2e/steps/gameMenu.steps.js new file mode 100644 index 00000000..879e63db --- /dev/null +++ b/webapp/e2e/steps/gameMenu.steps.js @@ -0,0 +1,36 @@ +const puppeteer = require('puppeteer'); +const { defineFeature, loadFeature } = require('jest-cucumber'); +const setDefaultOptions = require('expect-puppeteer').setDefaultOptions; + +const feature = loadFeature('./features/gameMenu.feature'); + +let page; +let browser; + +defineFeature(feature, test => { + + beforeAll(async () => { + browser = await puppeteer.launch({ + slowMo: 20, + defaultViewport: { width: 1920, height: 1080 }, + args: ['--window-size=1920,1080'] + }); + + page = await browser.newPage(); + setDefaultOptions({ timeout: 10000 }); + }); + + test('There should be visible three links', ({ given, then }) => { + given('I am on the the game menu', async () => { + await page.goto('http://localhost:3000/menu'); + await page.waitForSelector('.divMenu'); + }); + + then('Then three buttons should be visible', async () => { + //await expect(page).toMatchElement('.linkButton'); + const elements = await page.$$('.linkButton'); + expect(elements.length).toBeGreaterThan(0); // At least one element with class 'linkButton' + }); + }); + +}); From 6d29ff78a491337012d9596fd122853237db8235 Mon Sep 17 00:00:00 2001 From: uo289267 Date: Thu, 18 Apr 2024 13:25:38 +0200 Subject: [PATCH 27/40] Removed mock from ranking --- .../src/components/ranking/RankingRetriever.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/webapp/src/components/ranking/RankingRetriever.js b/webapp/src/components/ranking/RankingRetriever.js index d2c11001..41953d22 100644 --- a/webapp/src/components/ranking/RankingRetriever.js +++ b/webapp/src/components/ranking/RankingRetriever.js @@ -8,7 +8,7 @@ class RankingRetriever{ } async getTopTen() { - /* + try { const response = await axios.get(this.apiUrl + '/top10');//finding the top ten const receivedTopTenRanking = await response.data; @@ -18,8 +18,8 @@ class RankingRetriever{ console.log(error) throw new Error(error); - }*/ - + } + /* return { "usersCompetitiveStats": [ { @@ -73,10 +73,10 @@ class RankingRetriever{ "totalCompetitiveGames": 11 } ] - } + }*/ } async getMyPosition(user){ - /* + try { const response = await axios.get(this.apiUrl + '/'+user);//finding the top ten const receivedMyRanking = await response.data; @@ -86,13 +86,13 @@ class RankingRetriever{ console.log(error) throw new Error(error); - }*/ - + } + /* return { "_id": "myUser", "totalPoints": 250, "totalCompetitiveGames": 1 - }; + };*/ } From 8b72579419bf557d5faa956a8d7bae91181d4407 Mon Sep 17 00:00:00 2001 From: uo289267 Date: Thu, 18 Apr 2024 13:32:04 +0200 Subject: [PATCH 28/40] Fixing tests --- webapp/src/components/ranking/RankingView.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/webapp/src/components/ranking/RankingView.js b/webapp/src/components/ranking/RankingView.js index 2737ccf1..6d172024 100644 --- a/webapp/src/components/ranking/RankingView.js +++ b/webapp/src/components/ranking/RankingView.js @@ -17,7 +17,7 @@ const RankingView = () => { try { var ranking = await retriever.getTopTen(); setRankingData(ranking.usersCompetitiveStats); - var myrank = await retriever.getMyPosition("user.username"); + var myrank = await retriever.getMyPosition(user.username); setMyRankingData(myrank); console.log(myrank) } catch (error) { @@ -32,7 +32,7 @@ const RankingView = () => { return (
-

{t("ranking.ranking")}🏅

+

{t("ranking.ranking")}

{rankingData && rankingData.length > 0 && myRankingData ? (
From cd62261107be384719a743ec7b830212656466b1 Mon Sep 17 00:00:00 2001 From: uo289267 Date: Sat, 20 Apr 2024 14:14:30 +0200 Subject: [PATCH 29/40] Corrected Game Menu e2e --- package-lock.json | 17 ----------------- webapp/e2e/steps/gameMenu.steps.js | 4 ++-- 2 files changed, 2 insertions(+), 19 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8e7dbe69..b785ca01 100644 --- a/package-lock.json +++ b/package-lock.json @@ -560,23 +560,6 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.24.1.tgz", - "integrity": "sha512-szog8fFTUxBfw0b98gEWPaEqF42ZUD/T3bkynW/wtgx2p/XCP55WEsb+VosKceRSd6njipdZvNogqdtI4Q0chw==", - "dev": true, - "dependencies": { - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-simple-access": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, "node_modules/@babel/runtime": { "version": "7.23.9", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.9.tgz", diff --git a/webapp/e2e/steps/gameMenu.steps.js b/webapp/e2e/steps/gameMenu.steps.js index 879e63db..2f66b817 100644 --- a/webapp/e2e/steps/gameMenu.steps.js +++ b/webapp/e2e/steps/gameMenu.steps.js @@ -21,12 +21,12 @@ defineFeature(feature, test => { }); test('There should be visible three links', ({ given, then }) => { - given('I am on the the game menu', async () => { + given('I am on the game menu', async () => { await page.goto('http://localhost:3000/menu'); await page.waitForSelector('.divMenu'); }); - then('Then three buttons should be visible', async () => { + then('three buttons should be visible', async () => { //await expect(page).toMatchElement('.linkButton'); const elements = await page.$$('.linkButton'); expect(elements.length).toBeGreaterThan(0); // At least one element with class 'linkButton' From 34945b4456a89aa98f1bb899a571ea0f6977e5b0 Mon Sep 17 00:00:00 2001 From: uo289267 Date: Sat, 20 Apr 2024 18:07:03 +0200 Subject: [PATCH 30/40] GameMenu e2e --- webapp/e2e/steps/gameMenu.steps.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webapp/e2e/steps/gameMenu.steps.js b/webapp/e2e/steps/gameMenu.steps.js index 2f66b817..7f09955e 100644 --- a/webapp/e2e/steps/gameMenu.steps.js +++ b/webapp/e2e/steps/gameMenu.steps.js @@ -2,7 +2,7 @@ const puppeteer = require('puppeteer'); const { defineFeature, loadFeature } = require('jest-cucumber'); const setDefaultOptions = require('expect-puppeteer').setDefaultOptions; -const feature = loadFeature('./features/gameMenu.feature'); +const feature = loadFeature('../features/gameMenu.feature'); let page; let browser; From a5032570491bb614bb51bb691a45b46b25e0612b Mon Sep 17 00:00:00 2001 From: uo289267 Date: Sat, 20 Apr 2024 18:23:27 +0200 Subject: [PATCH 31/40] e2e fixes --- webapp/e2e/steps/gameMenu.steps.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webapp/e2e/steps/gameMenu.steps.js b/webapp/e2e/steps/gameMenu.steps.js index 7f09955e..2f66b817 100644 --- a/webapp/e2e/steps/gameMenu.steps.js +++ b/webapp/e2e/steps/gameMenu.steps.js @@ -2,7 +2,7 @@ const puppeteer = require('puppeteer'); const { defineFeature, loadFeature } = require('jest-cucumber'); const setDefaultOptions = require('expect-puppeteer').setDefaultOptions; -const feature = loadFeature('../features/gameMenu.feature'); +const feature = loadFeature('./features/gameMenu.feature'); let page; let browser; From 504232b43559917d8e2d40a4bfe11b3c8427cd6a Mon Sep 17 00:00:00 2001 From: uo289267 Date: Sat, 20 Apr 2024 19:18:02 +0200 Subject: [PATCH 32/40] Added e2e test --- webapp/e2e/features/gameMenu.feature | 4 ++++ webapp/e2e/steps/gameMenu.steps.js | 12 ++++++++++++ 2 files changed, 16 insertions(+) diff --git a/webapp/e2e/features/gameMenu.feature b/webapp/e2e/features/gameMenu.feature index 3953d899..a156fbbc 100644 --- a/webapp/e2e/features/gameMenu.feature +++ b/webapp/e2e/features/gameMenu.feature @@ -2,3 +2,7 @@ Feature: Game Menu page functionality Scenario: There should be visible three links Given I am on the game menu Then three buttons should be visible + Scenario: New Game should go to game configurator + Given I am on the game menu + When I click on New Game + Then I should be in the game configurator diff --git a/webapp/e2e/steps/gameMenu.steps.js b/webapp/e2e/steps/gameMenu.steps.js index 2f66b817..cc4230c2 100644 --- a/webapp/e2e/steps/gameMenu.steps.js +++ b/webapp/e2e/steps/gameMenu.steps.js @@ -32,5 +32,17 @@ defineFeature(feature, test => { expect(elements.length).toBeGreaterThan(0); // At least one element with class 'linkButton' }); }); + test('New Game should go to game configurator', ({ given, when, then }) => { + given('I am on the game menu', async () => { + await page.goto('http://localhost:3000/menu'); + await page.waitForSelector('.divMenu'); + }); + when('I click on New Game', async () => { + await page.click('.linkButton:first-child'); + }); + then('I should be in the game configurator', async () => { + await expect(page).toMatchElement('.GameConfiguratorDiv'); + }); + }); }); From b7249ce4cdc7a44448e715f1da7a05f2d3a4a3cb Mon Sep 17 00:00:00 2001 From: Mister-Mario Date: Sat, 20 Apr 2024 19:23:04 +0200 Subject: [PATCH 33/40] Added enpoint functionalities to return the ranking position of each user --- users/recordservice/record-service.js | 78 ++++++++++++---------- users/recordservice/record-service.test.js | 1 + 2 files changed, 44 insertions(+), 35 deletions(-) diff --git a/users/recordservice/record-service.js b/users/recordservice/record-service.js index 4528a2b6..dace39b2 100644 --- a/users/recordservice/record-service.js +++ b/users/recordservice/record-service.js @@ -1,7 +1,8 @@ const express = require('express'); const mongoose = require('mongoose'); const bodyParser = require('body-parser'); -const Record = require('./record-model') +const Record = require('./record-model'); +const { SystemUpdate } = require('@material-ui/icons'); const app = express(); const port = 8004; @@ -9,6 +10,10 @@ const port = 8004; // Middleware to parse JSON in request body app.use(bodyParser.json()); +var ranking = []; +var lastTime = new Date(); +const minTimeDifferenceInMiliseconds = 120_000; + // Connect to MongoDB const mongoUri = process.env.MONGODB_URI || 'mongodb://localhost:27017/userdb'; mongoose.connect(mongoUri); @@ -44,24 +49,8 @@ app.post('/record', async (req, res) => { app.get('/record/ranking/top10', async (req, res) => { try { - const usersCompetitiveStats = await Record.aggregate([ - // Unwind the games array to work with each game separately - { $unwind: "$games" }, - // Match only competitive games - { $match: { "games.competitive": true } }, - // Group by user and calculate total points and total competitive games per user - { - $group: { - _id: "$user", - totalPoints: { $sum: "$games.points" }, - totalCompetitiveGames: { $sum: 1 } // Count the number of competitive games - } - }, - // Sort by total points in descending order (top 1 will have the highest points) - { $sort: { totalPoints: -1 } }, - // Limit to the top 10 - { $limit: 10 } - ]); + let usersRanking = await getRanking(); + let usersCompetitiveStats = usersRanking.slice(0, 10); res.json({usersCompetitiveStats: usersCompetitiveStats }); } catch (err) { @@ -72,25 +61,13 @@ app.get('/record/ranking/top10', async (req, res) => { app.get('/record/ranking/:user', async (req, res) => { try { const user = req.params.user.toString(); - //Gives back an array of 1 user - const userCompetitiveStats = await Record.aggregate([ - { $match: { user: user } }, - // Unwind the games array to work with each game separately - { $unwind: "$games" }, - // Match only competitive games - { $match: { "games.competitive": true } }, - // Group by user and calculate total points and total competitive games per user - { - $group: { - _id: "$user", - totalPoints: { $sum: "$games.points" }, - totalCompetitiveGames: { $sum: 1 } // Count the number of competitive games - } - } - ]); + + let usersRanking = await getRanking(); + let userCompetitiveStats = usersRanking.filter(userData => userData._id === user); res.json({userCompetitiveStats: userCompetitiveStats[0] }); } catch (err) { + console.log(err) res.status(500).send(); } }); @@ -108,6 +85,37 @@ app.get('/record/:user', async (req, res) => { } }); + +async function getRanking(){ + const nowTime = new Date(); + let timeDifferenceInMiliseconds = nowTime - lastTime; + if(ranking.length == 0 || timeDifferenceInMiliseconds > minTimeDifferenceInMiliseconds){ + ranking = await Record.aggregate([ + // Unwind the games array to work with each game separately + { $unwind: "$games" }, + // Match only competitive games + { $match: { "games.competitive": true } }, + // Group by user and calculate total points and total competitive games per user + { + $group: { + _id: "$user", + totalPoints: { $sum: "$games.points" }, + totalCompetitiveGames: { $sum: 1 } // Count the number of competitive games + } + }, + // Sort by total points in descending order (top 1 will have the highest points) + { $sort: { totalPoints: -1 } } + ]); + + //The operator ... dumps the user json making it {_id , totalPoints, totalCompetitiveGames, position} + ranking = ranking.map((user, index) => ({ ...user, position: index + 1 })); + lastTime = new Date(); + } + + return ranking; + +} + const server = app.listen(port, () => { console.log(`Record Service listening at http://localhost:${port}`); }); diff --git a/users/recordservice/record-service.test.js b/users/recordservice/record-service.test.js index a42d1978..5594f781 100644 --- a/users/recordservice/record-service.test.js +++ b/users/recordservice/record-service.test.js @@ -239,6 +239,7 @@ describe('Record Service', () => { const userStats = responseGet.body.userCompetitiveStats; expect(userStats).toHaveProperty('_id', 'user1'); + expect(userStats).toHaveProperty('position', 10); expect(userStats).toHaveProperty('totalCompetitiveGames', 2); expect(userStats).toHaveProperty('totalPoints', 20); //i * 10 * totalCompetitiveGames , i = 2 }); From 28946581af210b7424e412392a7d3c1cf5774b69 Mon Sep 17 00:00:00 2001 From: uo289267 Date: Sat, 20 Apr 2024 19:24:38 +0200 Subject: [PATCH 34/40] Fixed e2e --- webapp/e2e/steps/gameMenu.steps.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webapp/e2e/steps/gameMenu.steps.js b/webapp/e2e/steps/gameMenu.steps.js index cc4230c2..215af2f7 100644 --- a/webapp/e2e/steps/gameMenu.steps.js +++ b/webapp/e2e/steps/gameMenu.steps.js @@ -38,7 +38,7 @@ defineFeature(feature, test => { await page.waitForSelector('.divMenu'); }); when('I click on New Game', async () => { - await page.click('.linkButton:first-child'); + await page.click('.linkButton'); }); then('I should be in the game configurator', async () => { await expect(page).toMatchElement('.GameConfiguratorDiv'); From 489a6875d6c01e7c9078fc940ea9fb862d28be70 Mon Sep 17 00:00:00 2001 From: Mister-Mario Date: Sat, 20 Apr 2024 19:28:33 +0200 Subject: [PATCH 35/40] Removed weird import, probably VS Code stuff --- users/recordservice/record-service.js | 1 - 1 file changed, 1 deletion(-) diff --git a/users/recordservice/record-service.js b/users/recordservice/record-service.js index dace39b2..cf94544a 100644 --- a/users/recordservice/record-service.js +++ b/users/recordservice/record-service.js @@ -2,7 +2,6 @@ const express = require('express'); const mongoose = require('mongoose'); const bodyParser = require('body-parser'); const Record = require('./record-model'); -const { SystemUpdate } = require('@material-ui/icons'); const app = express(); const port = 8004; From 2f4e6a3198207a3d91023c808519dde6a6e2c8b9 Mon Sep 17 00:00:00 2001 From: Mister-Mario Date: Sun, 21 Apr 2024 09:33:59 +0200 Subject: [PATCH 36/40] Made view work with the new endpoint --- .../questionView/CreationHistoricalRecord.js | 37 +++++++++---------- .../components/ranking/RankingRetriever.js | 4 +- webapp/src/components/ranking/RankingView.js | 4 +- .../components/ranking/RankingView.test.js | 7 +++- 4 files changed, 25 insertions(+), 27 deletions(-) diff --git a/webapp/src/components/questionView/CreationHistoricalRecord.js b/webapp/src/components/questionView/CreationHistoricalRecord.js index 68ceb4f5..3bdb6370 100644 --- a/webapp/src/components/questionView/CreationHistoricalRecord.js +++ b/webapp/src/components/questionView/CreationHistoricalRecord.js @@ -39,29 +39,26 @@ class CreationHistoricalRecord{ async sendRecord(user) { - const apiUrl = (process.env.REACT_APP_API_ENDPOINT || 'http://localhost:8000') + "/record"; - - const body = { - user:user, - game:this.record.game - } - try { + const apiUrl = (process.env.REACT_APP_API_ENDPOINT || 'http://localhost:8000') + "/record"; + + const body = { + user: user, + game: this.record.game + }; + + try { const response = await axios.post(apiUrl, body, { - headers: { - 'Content-Type': 'application/json' - } + headers: { + 'Content-Type': 'application/json' + } }); - - if (!response.ok) { - throw new Error('Error al enviar el registro'); - } - - const data = await response.json(); - console.log(data); - } catch (error) { - console.error('Error:', error); - } + + + console.log('Registro enviado:', response.data); + } catch (error) { + console.error('Error al enviar el registro:', error.message); } + } } export default CreationHistoricalRecord; diff --git a/webapp/src/components/ranking/RankingRetriever.js b/webapp/src/components/ranking/RankingRetriever.js index 41953d22..e22d2e08 100644 --- a/webapp/src/components/ranking/RankingRetriever.js +++ b/webapp/src/components/ranking/RankingRetriever.js @@ -12,7 +12,6 @@ class RankingRetriever{ try { const response = await axios.get(this.apiUrl + '/top10');//finding the top ten const receivedTopTenRanking = await response.data; - console.log(receivedTopTenRanking) return receivedTopTenRanking; } catch (error) { console.log(error) @@ -80,8 +79,7 @@ class RankingRetriever{ try { const response = await axios.get(this.apiUrl + '/'+user);//finding the top ten const receivedMyRanking = await response.data; - console.log(receivedMyRanking) - return receivedMyRanking; + return receivedMyRanking.userCompetitiveStats; } catch (error) { console.log(error) throw new Error(error); diff --git a/webapp/src/components/ranking/RankingView.js b/webapp/src/components/ranking/RankingView.js index 6d172024..f59a53ce 100644 --- a/webapp/src/components/ranking/RankingView.js +++ b/webapp/src/components/ranking/RankingView.js @@ -25,7 +25,7 @@ const RankingView = () => { } } - if(rankingData==null){ + if(rankingData==null || myRankingData == null){ getRanking(); } @@ -57,7 +57,7 @@ const RankingView = () => { - + diff --git a/webapp/src/components/ranking/RankingView.test.js b/webapp/src/components/ranking/RankingView.test.js index dd0c05fd..a0a5762a 100644 --- a/webapp/src/components/ranking/RankingView.test.js +++ b/webapp/src/components/ranking/RankingView.test.js @@ -94,10 +94,13 @@ describe('RankingView component', () => { }); mockAxios.onGet('http://localhost:8000/record/ranking/myUser').reply(200, - { + {usersCompetitiveStats: + { "_id": "myUser", "totalPoints": 250, - "totalCompetitiveGames": 1 + "totalCompetitiveGames": 1, + "position":10 + } } ); const user = { username: 'myUser' }; From e497488e9c0f4a54a8b104863fa21392b7e5a7af Mon Sep 17 00:00:00 2001 From: uo289267 Date: Sun, 21 Apr 2024 11:04:35 +0200 Subject: [PATCH 37/40] Added search in Ranking --- webapp/src/components/ranking/RankingView.js | 29 ++++++++++++++++++-- webapp/src/custom.css | 18 ++++++++++++ webapp/src/translations/en/global.json | 4 ++- webapp/src/translations/es/global.json | 4 ++- 4 files changed, 51 insertions(+), 4 deletions(-) diff --git a/webapp/src/components/ranking/RankingView.js b/webapp/src/components/ranking/RankingView.js index 6d172024..136a84aa 100644 --- a/webapp/src/components/ranking/RankingView.js +++ b/webapp/src/components/ranking/RankingView.js @@ -10,6 +10,8 @@ const retriever = new RankingRetriever(); const RankingView = () => { const [rankingData, setRankingData] = useState(null); const [myRankingData, setMyRankingData] = useState(null); + const [searchTerm, setSearchTerm] = useState(""); // Nuevo estado para el término de búsqueda + const[t] = useTranslation("global"); const {user} = useUserContext(); @@ -17,14 +19,25 @@ const RankingView = () => { try { var ranking = await retriever.getTopTen(); setRankingData(ranking.usersCompetitiveStats); - var myrank = await retriever.getMyPosition(user.username); + var myrank = await retriever.getUser(user.username); setMyRankingData(myrank); console.log(myrank) } catch (error) { console.log(error); } } - + const handleSearch = async (e) => { + e.preventDefault(); + if(searchTerm.length!==0){ + try { + const rank = await retriever.getUser(searchTerm); + setMyRankingData(rank); + } catch (error) { + console.log(error); + } + } + + } if(rankingData==null){ getRanking(); } @@ -33,7 +46,18 @@ const RankingView = () => {

{t("ranking.ranking")}

+
+ setSearchTerm(e.target.value)} + placeholder={t("ranking.enter_username")} + /> + + {rankingData && rankingData.length > 0 && myRankingData ? ( + <> +
0{myRankingData.position} {myRankingData._id} {myRankingData.totalPoints} {myRankingData.totalCompetitiveGames}
@@ -64,6 +88,7 @@ const RankingView = () => {
+ ) : ( < Loader /> )} diff --git a/webapp/src/custom.css b/webapp/src/custom.css index 8771f7dc..4c819c13 100644 --- a/webapp/src/custom.css +++ b/webapp/src/custom.css @@ -1655,3 +1655,21 @@ h2{ .table tbody tr:last-child td:last-child { border-right: 2px solid rgba(205, 187, 187, 0.455); /* Borde derecho */ } + +/* Estilo para el cuadro de búsqueda */ +input[type="text"] { + border: 2px solid #636262; /* Borde sólido */ + padding: 8px; /* Espaciado interno */ + font-size: 16px; /* Tamaño de letra */ + color: #e5e0e0; /* Color de letra */ + background-color: #2a2929; /* Color de fondo */ +} + +/* Estilo para el botón */ +.search > button{ + font-size: 18px; /* Tamaño de letra */ + width: 7em; + height: 2em; +} + + diff --git a/webapp/src/translations/en/global.json b/webapp/src/translations/en/global.json index 688d269c..c076edbd 100644 --- a/webapp/src/translations/en/global.json +++ b/webapp/src/translations/en/global.json @@ -103,7 +103,9 @@ "position":"Position", "username":"Username", "points":"Points", - "num_games":"Competitive games" + "num_games":"Competitive games", + "search":"Search", + "enter_username":"Enter Username..." }, "error":{ "error":"Error", diff --git a/webapp/src/translations/es/global.json b/webapp/src/translations/es/global.json index f0a2c80c..898263d1 100644 --- a/webapp/src/translations/es/global.json +++ b/webapp/src/translations/es/global.json @@ -111,7 +111,9 @@ "position":"Posición", "username":"Username", "points":"Puntos", - "num_games":"Juegos Competitivos" + "num_games":"Juegos Competitivos", + "search":"Buscar", + "enter_username":"Inserta usuario..." } } From 3801d71f82fe5b8b10c0ca47203369f8e33fbc25 Mon Sep 17 00:00:00 2001 From: Mister-Mario Date: Sun, 21 Apr 2024 11:46:26 +0200 Subject: [PATCH 38/40] Tested that the search user in ranking works --- .../components/ranking/RankingRetriever.js | 2 +- webapp/src/components/ranking/RankingView.js | 83 ++++++++++--------- webapp/src/custom.css | 7 +- 3 files changed, 51 insertions(+), 41 deletions(-) diff --git a/webapp/src/components/ranking/RankingRetriever.js b/webapp/src/components/ranking/RankingRetriever.js index e22d2e08..a84de7e6 100644 --- a/webapp/src/components/ranking/RankingRetriever.js +++ b/webapp/src/components/ranking/RankingRetriever.js @@ -74,7 +74,7 @@ class RankingRetriever{ ] }*/ } - async getMyPosition(user){ + async getUser(user){ try { const response = await axios.get(this.apiUrl + '/'+user);//finding the top ten diff --git a/webapp/src/components/ranking/RankingView.js b/webapp/src/components/ranking/RankingView.js index e7c76f45..e88581ec 100644 --- a/webapp/src/components/ranking/RankingView.js +++ b/webapp/src/components/ranking/RankingView.js @@ -46,48 +46,53 @@ const RankingView = () => {

{t("ranking.ranking")}

-
- setSearchTerm(e.target.value)} - placeholder={t("ranking.enter_username")} - /> - -
{rankingData && rankingData.length > 0 && myRankingData ? ( <> - - - - - - - - - - - - {rankingData.map((user, index) => ( - - - - - + +
{t("ranking.position")}{t("ranking.username")}{t("ranking.points")}{t("ranking.num_games")}
{index + 1}{user._id}{user.totalPoints}{user.totalCompetitiveGames}
+ + + + + + - ))} - {/* Blank row */} - - - - - - - - - - -
{t("ranking.position")}{t("ranking.username")}{t("ranking.points")}{t("ranking.num_games")}
{myRankingData.position}{myRankingData._id}{myRankingData.totalPoints}{myRankingData.totalCompetitiveGames}
+ + + {rankingData.map((user, index) => ( + + {index + 1} + {user._id} + {user.totalPoints} + {user.totalCompetitiveGames} + + ))} + {/* Blank row */} + + + setSearchTerm(e.target.value)} + placeholder={t("ranking.enter_username")} + /> + + +
+ +
+ + + + + {myRankingData.position} + {myRankingData._id} + {myRankingData.totalPoints} + {myRankingData.totalCompetitiveGames} + + + + ) : ( < Loader /> diff --git a/webapp/src/custom.css b/webapp/src/custom.css index 4c819c13..f6660f91 100644 --- a/webapp/src/custom.css +++ b/webapp/src/custom.css @@ -1598,6 +1598,11 @@ h2{ box-shadow: 0 0 10px rgba(197, 191, 191, 0.726);*/ } +.table tbody tr.penultimate-row td div{ + background-color: #171717; + padding: 0.5em; +} + .table th,td{ padding-left: 30px; padding-right: 30px; @@ -1666,7 +1671,7 @@ input[type="text"] { } /* Estilo para el botón */ -.search > button{ +#search { font-size: 18px; /* Tamaño de letra */ width: 7em; height: 2em; From e407b7b4d310d7a6af26efbb54addee092db9108 Mon Sep 17 00:00:00 2001 From: Mister-Mario Date: Sun, 21 Apr 2024 12:12:37 +0200 Subject: [PATCH 39/40] Made the default search value the username and fixed bad mock on tests --- webapp/src/components/ranking/RankingView.js | 9 +++++---- webapp/src/components/ranking/RankingView.test.js | 15 ++++++++------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/webapp/src/components/ranking/RankingView.js b/webapp/src/components/ranking/RankingView.js index e88581ec..f2aad16e 100644 --- a/webapp/src/components/ranking/RankingView.js +++ b/webapp/src/components/ranking/RankingView.js @@ -8,12 +8,14 @@ import { useUserContext } from '../loginAndRegistration/UserContext'; const retriever = new RankingRetriever(); const RankingView = () => { + const[t] = useTranslation("global"); + const {user} = useUserContext(); + const [rankingData, setRankingData] = useState(null); const [myRankingData, setMyRankingData] = useState(null); - const [searchTerm, setSearchTerm] = useState(""); // Nuevo estado para el término de búsqueda + const [searchTerm, setSearchTerm] = useState(user.username); + - const[t] = useTranslation("global"); - const {user} = useUserContext(); const getRanking = async () => { try { @@ -21,7 +23,6 @@ const RankingView = () => { setRankingData(ranking.usersCompetitiveStats); var myrank = await retriever.getUser(user.username); setMyRankingData(myrank); - console.log(myrank) } catch (error) { console.log(error); } diff --git a/webapp/src/components/ranking/RankingView.test.js b/webapp/src/components/ranking/RankingView.test.js index a0a5762a..76c0506a 100644 --- a/webapp/src/components/ranking/RankingView.test.js +++ b/webapp/src/components/ranking/RankingView.test.js @@ -18,24 +18,23 @@ global.i18en = i18en; const mockAxios = new MockAdapter(axios); describe('RankingView component', () => { - + const user = { username: 'myUser' }; it('renders title', () => { act(()=>{ - render(); + render(); }) const text = screen.getByText(i18en.t('ranking.ranking')); expect(text).toBeInTheDocument(); }); it('renders Loading if the call to the gateway has not been done', () => { act(()=>{ - render(); + render(); }) const text = screen.getByText('Loading...'); expect(text).toBeInTheDocument(); }); }); - describe('RankingView component with endpoint', ()=>{ mockAxios.onGet('http://localhost:8000/record/ranking/top10').reply(200, { @@ -94,7 +93,7 @@ describe('RankingView component', () => { }); mockAxios.onGet('http://localhost:8000/record/ranking/myUser').reply(200, - {usersCompetitiveStats: + {userCompetitiveStats: { "_id": "myUser", "totalPoints": 250, @@ -111,11 +110,12 @@ describe('RankingView component', () => { await render(); }) - await waitFor(() => expect(screen.getByText(i18en.t('ranking.position'))).toBeInTheDocument()); + await waitFor(() => expect(screen.getByText(i18en.t('ranking.position'))).toBeInTheDocument()); expect(screen.getByText(i18en.t('ranking.username'))).toBeInTheDocument() expect(screen.getByText(i18en.t('ranking.points'))).toBeInTheDocument() expect(screen.getByText(i18en.t('ranking.num_games'))).toBeInTheDocument() - }); + }); + it('renders position all users usernames',async ()=>{ await act(async () =>{ await render(); @@ -177,5 +177,6 @@ describe('RankingView component', () => { expect(screen.getAllByText(/1/).length).toBeGreaterThanOrEqual(2);//hay dos pq hay una posicion }); + }) From 25d65b92242ff6c0508b673797c40a72da4e73b0 Mon Sep 17 00:00:00 2001 From: Mister-Mario Date: Sun, 21 Apr 2024 12:44:48 +0200 Subject: [PATCH 40/40] Added test case to increase test coverage --- .../questionView/QuestionView.test.js | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/webapp/src/components/questionView/QuestionView.test.js b/webapp/src/components/questionView/QuestionView.test.js index a3a34531..90248453 100644 --- a/webapp/src/components/questionView/QuestionView.test.js +++ b/webapp/src/components/questionView/QuestionView.test.js @@ -51,9 +51,9 @@ global.i18en = i18en; describe('Question View component', () => { - beforeEach(() => { - mockAxios.reset(); - }); + mockAxios.onGet('http://localhost:8000/questions/en').reply(200, + [{question: "What is the population of Oviedo?", + answers: ["225089","272357","267855","231841"]}]); it('shows the no_questions_message as the endpoint does not exist',async () => { render(); @@ -65,10 +65,6 @@ describe('Question View component', () => { // Test for sound functionality it('speaks the question when the speaker button is clicked', async () => { - const questionText = "What is the population of Oviedo?"; - mockAxios.onGet('http://localhost:8000/questions/en').reply(200, - [{question: questionText, - answers: ["225089","272357","267855","231841"]}]); await act(async () => { render(); @@ -83,10 +79,6 @@ describe('Question View component', () => { }); it('shows a question and answers',async () => { - - mockAxios.onGet('http://localhost:8000/questions/en').reply(200, - [{question: "What is the population of Oviedo?", - answers: ["225089","272357","267855","231841"]}]); //It gives an error as we are not wrapping it by act, however by doing this we simulate a no questions situation await act(async () =>{ @@ -104,9 +96,6 @@ describe('Question View component', () => { }); it('shows colors to reveal correct answer and it sounds', async () => { setupAudioMock(); - mockAxios.onGet('http://localhost:8000/questions/en').reply(200, - [{question: "What is the population of Oviedo?", - answers: ["225089","272357","267855","231841"]}]); await act(async () =>{ await render(); @@ -125,9 +114,6 @@ describe('Question View component', () => { }); it('shows colors to reveal false answer and it sounds', async () => { setupAudioMock() - mockAxios.onGet('http://localhost:8000/questions/en').reply(200, - [{question: "What is the population of Oviedo?", - answers: ["225089","272357","267855","231841"]}]); await act(async () =>{ await render(); @@ -146,9 +132,6 @@ describe('Question View component', () => { it('shows timer and tiktak sound', async () => { setupAudioMock() - mockAxios.onGet('http://localhost:8000/questions/en').reply(200, - [{question: "What is the population of Oviedo?", - answers: ["225089","272357","267855","231841"]}]); await act(async () =>{ await render(); @@ -158,7 +141,24 @@ describe('Question View component', () => { const timerElement = screen.getByText(new RegExp(`(\\d+) ${i18en.t('questionView.seconds')}`)); expect(timerElement).toBeInTheDocument(); // Verificar que el temporizador esté presente en el DOM - }); + }); + + it('shows finish game review',async () => { + mockAxios.onGet('http://localhost:8000/questions/en').reply(200, []); + mockAxios.onPost('http://localhost:8000/record').reply(200, {user:'myUser'}); + + const user = { username: 'myUser' }; + + //It gives an error as we are not wrapping it by act, however by doing this we simulate a no questions situation + await act(async () =>{ + await render(); + }) + + await waitFor(() => expect(screen.getByText(i18en.t('questionView.finished_game'))).toBeInTheDocument()); + + expect(screen.getByText('What is the population of Oviedo?')).toBeInTheDocument() + + }); // it('renders end message when countdown completes', async() => {