diff --git a/gameservice/game-service.js b/gameservice/game-service.js index f551e7a3..807fe92c 100644 --- a/gameservice/game-service.js +++ b/gameservice/game-service.js @@ -25,11 +25,11 @@ app.use(express.json()); // Middleware to enable CORS (cross-origin resource sharing). In order for the API to be accessible by other origins (domains). app.use(cors()); -var gameId = 0; +let gameId = 0; app.get('/generateGame', async (req, res) => { console.log("Llegamos a crear un id del juego") - var gameId = generateAleatoryString() + let gameId = generateAleatoryString() res.json(gameId) }) @@ -37,8 +37,6 @@ app.get('/generateGame', async (req, res) => { // Route for getting questions app.get('/questions', async (req, res) => { try { - // TODO: Implement logic to fetch questions from MongoDB and send response - // const questions = await Question.find() const questionGenerated = await axios.get(questionService + req.url); res.json(questionGenerated.data); } catch (error) { @@ -50,11 +48,11 @@ app.get('/questions', async (req, res) => { app.post('/storeGame', async (req, res) => { try { //hay que preparar los datos para enviarlos al servicio - var id = req.body.id - var username = req.body.username - var points = req.body.points - var questions = req.body.questions - var avgtime = req.body.avgtime + let id = req.body.id + let username = req.body.username + let points = req.body.points + let questions = req.body.questions + let avgtime = req.body.avgtime console.log("Vamos a guardar resultado") const store = await axios.post(`${userStatsService}/history/game`, {id, points, username, questions, avgtime}) console.log("Guardamos resultado") @@ -64,6 +62,7 @@ app.post('/storeGame', async (req, res) => { } }) +//FALTA app.get('/topics', async (req, res) => { try { const topics = await axios.get(`${questionService}/topics`) diff --git a/gameservice/openapi.yaml b/gameservice/openapi.yaml index bb91bb3b..121f5317 100644 --- a/gameservice/openapi.yaml +++ b/gameservice/openapi.yaml @@ -9,10 +9,11 @@ servers: - url: http://gameservice:8005 description: Production server paths: - /generateGameUnlimitedQuestions: + + /generateGame: get: - summary: Generate the id for the unlimited game questions. - operationId: generateGameUnlimitedQuestions + summary: Generate the id for the game. + operationId: generateGame requestBody: required: true content: @@ -30,6 +31,7 @@ paths: id: type: string description: The id of the game created. + example: Game id created (sqeweg12er56yg46u89k) '500': description: Internal server error. content: @@ -41,10 +43,11 @@ paths: type: string description: Error information. example: Internal Server Error - /gameUnlimitedQuestion: + + /questions: get: - summary: Generate questions for the unlimited game questions. - operationId: gameUnlimitedQuestions + summary: Generate questions for the game. + operationId: questions requestBody: required: true content: @@ -67,13 +70,15 @@ paths: answers: type: List description: List with the possible answers. - example: + example: Madrid, Paris, Roma o Londres. question: type: String description: The question that is going to be done + example: ¿Capital de España? correctAnswer: type: String - description: The correct answer for the question + description: The correct answer for the question + example: Madrid. '500': description: Internal server error. content: @@ -103,20 +108,28 @@ paths: username: type: string description: The name if the user. + example: username. points: type: integer description: The number of points that the user score. + example: 1000. questions: answers: type: List description: List with the possible answers. - example: + example: Madrid, Paris, Roma o Londres. question: type: String description: The question that is going to be done + example: ¿Capital de España? correctAnswer: type: String description: The correct answer for the question + example: Madrid + avgtime: + type: integer + description: The average time taken to answer questions in seconds. + example: 23 responses: '200': description: Question created and returned successfuly. @@ -127,12 +140,12 @@ paths: properties: id: type: string - description: The question that is going to bre realized. - example: + description: The question that is going to be realized. + example: ¿Capital de España? answers: type: List description: List with the possible answers. - example: + example: Madrid, Paris, Roma o Londres. '500': description: Internal server error. content: @@ -143,4 +156,4 @@ paths: error: type: string description: Error information. - example: Internal Server Error \ No newline at end of file + example: Internal Server Error diff --git a/gatewayservice/gateway-service.js b/gatewayservice/gateway-service.js index 9eb77b64..5dfcd8dd 100644 --- a/gatewayservice/gateway-service.js +++ b/gatewayservice/gateway-service.js @@ -76,15 +76,6 @@ app.get('/history/questions', async (req, res) => { } }) -// app.get(`/questions`, async (req, res) => { -// try { -// const response = await axios.get(questionsGeneratorServiceUrl+`/questions`); -// res.json(response.data); -// } catch (error) { -// res.status(error.response.status).json({ error: error.response.data.error }); -// } -// }) - app.get('/generateGame', async (req, res) => { try { const response = await axios.get(gameService + '/generateGame') @@ -123,6 +114,7 @@ app.post('/storeGame', async (req, res) => { } }) +//FALTA ??? app.get('/topics', async (req, res) => { try { const response = await axios.get(`${gameService}/topics`) diff --git a/gatewayservice/openapi.yaml b/gatewayservice/openapi.yaml index 95142c1a..32c2c8bb 100644 --- a/gatewayservice/openapi.yaml +++ b/gatewayservice/openapi.yaml @@ -39,6 +39,7 @@ paths: username: type: string description: User ID + example: student password: type: string description: Hashed password @@ -142,6 +143,7 @@ paths: type: string description: Error information. example: Internal Server Error + /history/games/:username: get: summary: Access to de history of games of the user. @@ -188,10 +190,53 @@ paths: type: string description: Error information. example: Internal Server Error - /generateGameUnlimitedQuestions: + + /history/questions: get: - summary: Generate the id for the unlimited game questions. - operationId: generateGameUnlimitedQuestions + summary: Returns the the stored questions. + operationId: gethistoryQuestions + requestBody: + required: true + content: + application/json: + schema: + type: object + responses: + '200': + description: Question stored correctly and returns it. + content: + application/json: + schema: + type: object + properties: + token: + type: string + description: User token. + example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI2NWY3NTZkYjNmYTIyZDIyN2E0YjdjN2QiLCJpYXQiOjE3MTA3MDg3NDUsImV4cCI6MTcxMDcxMjM0NX0.VMG_5DOyQ4GYlJQRcu1I6ICG1IGzuo2Xuei093ONHxw + questions: + type: List + description: Question. + example: ¿Cual es la capital de España?, ¿Cual es la capital de Francia? + createdAt: + type: string + description: Creation date. + example: '2024-03-17T20:47:23.935Z' + '500': + description: Internal server error. + content: + application/json: + schema: + type: object + properties: + error: + type: string + description: Error information. + example: Internal Server Error + + /generateGame: + get: + summary: Generate the id for the game. + operationId: generateGame requestBody: required: true content: @@ -209,6 +254,7 @@ paths: id: type: string description: The id of the game created. + example: sqeweg12er56yg46u89k token: type: string description: User token. @@ -228,10 +274,11 @@ paths: type: string description: Error information. example: Internal Server Error - /gameUnlimitedQuestion: + + /questions: get: - summary: Generate questions for the unlimited game questions. - operationId: gameUnlimitedQuestions + summary: Generate questions for the game. + operationId: questions requestBody: required: true content: @@ -254,13 +301,15 @@ paths: answers: type: List description: List with the possible answers. - example: + example: Madrid, Paris, Roma o Londres question: type: String description: The question that is going to be done + example: ¿Cual es la capital de españa? correctAnswer: type: String description: The correct answer for the question + example: Madrid '500': description: Internal server error. content: @@ -272,6 +321,7 @@ paths: type: string description: Error information. example: Internal Server Error + /storeGame: post: summary: Stores the actual game in the database. @@ -290,20 +340,28 @@ paths: username: type: string description: The name if the user. + example: student points: type: integer description: The number of points that the user score. + example: 1000 questions: answers: type: List description: List with the possible answers. - example: + example: Madrid, Paris, Roma o Londres question: type: String description: The question that is going to be done + example: ¿Cual es la capital de españa? correctAnswer: type: String description: The correct answer for the question + example: Madrid + avgtime: + type: integer + description: average time to answer the questions + example: 23 responses: '200': description: Question created and returned successfuly. @@ -315,11 +373,11 @@ paths: id: type: string description: The question that is going to bre realized. - example: + example: sqeweg12er56yg46u89k answers: type: List description: List with the possible answers. - example: + example: Madrid, Paris, Roma o Londres '500': description: Internal server error. content: diff --git a/storeQuestionService/openapi.yaml b/storeQuestionService/openapi.yaml index a2f75789..e40b03c6 100644 --- a/storeQuestionService/openapi.yaml +++ b/storeQuestionService/openapi.yaml @@ -23,17 +23,19 @@ paths: pregunta: type: string description: The question. - example: + example: ¿Cual es la capital de España? respuesta_correcta: type: string description: The correct answer. - example: + example: Madrid respuestas_incorrectas: type: List description: The other possible answers. + example: Paris, Roma, Londres createdAt: type: string - description: Creation date. + description: Creation date. + example: '2024-03-17T20:47:23.935Z' responses: '200': description: Question stored correctly and returns it. @@ -49,6 +51,7 @@ paths: question: type: Question description: Question. + example: ¿Cual es la capital de España? createdAt: type: string description: Creation date. @@ -78,17 +81,19 @@ paths: preguntas: type: string description: The question. - example: + example: ¿Cual es la capital de España? respuesta_correcta: type: string description: The correct answer. - example: + example: Madrid respuestas_incorrectas: type: List description: The other possible answers. + example: Paris, Roma, Londres createdAt: type: string - description: Creation date. + description: Creation date. + example: '2024-03-17T20:47:23.935Z' responses: '200': description: Questions stored correctly and returns its. @@ -104,6 +109,7 @@ paths: questions: type: List description: Question. + example: ¿Cual es la capital de España?, ¿Cual es la capital de Francia? createdAt: type: string description: Creation date. @@ -143,6 +149,7 @@ paths: questions: type: List description: Question. + example: ¿Cual es la capital de España?, ¿Cual es la capital de Francia? createdAt: type: string description: Creation date. diff --git a/storeQuestionService/store-q-service.js b/storeQuestionService/store-q-service.js index 639b5250..314c6983 100644 --- a/storeQuestionService/store-q-service.js +++ b/storeQuestionService/store-q-service.js @@ -78,15 +78,6 @@ app.get('/history/questions', async (req, res) => { try { const questions = await Question.find({}); // Get all questions res.json(questions.sort(question => question.createdAt).reverse()); - /*res.json([{ //FORMATO VIEJO - question: '¿Cuál es la capital de la comunidad autónoma de Casstilla y León?', - answers: ['Segovia','León','Valladolid','Ninguna'], - }]);*/ - /*res.json([{ - pregunta: '¿Cuál es la capital de la comunidad autónoma de Castilla y León?', - respuesta_correcta: 'Ninguna', - respuestas_incorrectas: ['Segovia','León','Valladolid'] - }]);*/ } catch (error) { res.status(500).json({ error: error.message || 'An error occurred'}); } diff --git a/userStatsService/openapi.yaml b/userStatsService/openapi.yaml index e2a5dccf..13dfc625 100644 --- a/userStatsService/openapi.yaml +++ b/userStatsService/openapi.yaml @@ -27,9 +27,11 @@ paths: username: type: string description: The name if the user. + example: student points: type: integer description: The number of points that the user score. + example: 1000 questions: type: array items: @@ -38,17 +40,21 @@ paths: question: type: string description: The question that is going to be done. + example: ¿Cuál es la capital de España? correctAnswer: type: string description: The correct answer for the question. + example: Madrid answers: type: array items: type: string description: List with the possible answers. + example: Madrid, Paris, Roma o Londres (en dif items) avgtime: type: integer description: The average time taken to answer questions in seconds. + example: 23 responses: '200': description: Game created and returned successfully. @@ -64,12 +70,15 @@ paths: username: type: string description: The name if the user. + example: student points: type: integer description: The number of points that the user scored. + example: 1000 avgtime: type: integer description: The average time taken to answer questions in seconds. + example: 23 questions: type: array items: @@ -78,14 +87,17 @@ paths: question: type: string description: The question that was done. + example: ¿Cuál es la capital de España? correctAnswer: type: string description: The correct answer for the question. + example: Madrid answers: type: array items: type: string description: List with the possible answers. + example: Madrid, Paris, Roma o Londres (en diferentes items) createdAt: type: string format: date-time @@ -113,6 +125,7 @@ paths: type: string description: Error information. example: Internal Server Error + /history/games/{username}: get: summary: Access to the history of games of the user. @@ -124,6 +137,7 @@ paths: description: Username of the user. schema: type: string + example: student responses: '200': description: Information of games returned successfully. @@ -145,9 +159,11 @@ paths: points: type: integer description: The number of points that the user scored. + example: 1000 avgtime: type: integer description: The average time taken to answer questions in seconds. + example: 23 questions: type: array items: @@ -156,14 +172,17 @@ paths: question: type: string description: The question that was done. + example: ¿Cuál es la capital de España? correctAnswer: type: string description: The correct answer for the question. + example: Madrid answers: type: array items: type: string description: List with the possible answers. + example: Madrid, Paris, Roma o Londres (en diferentes items) createdAt: type: string format: date-time @@ -191,6 +210,7 @@ paths: type: string description: Error information. example: Internal Server Error + /history/games: get: summary: Get the history of all games. @@ -216,9 +236,11 @@ paths: points: type: integer description: The number of points that the user scored. + example: 1000 avgtime: type: integer description: The average time taken to answer questions in seconds. + example: 23 questions: type: array items: @@ -227,14 +249,17 @@ paths: question: type: string description: The question that was done. + example: ¿Cuál es la capital de España? correctAnswer: type: string description: The correct answer for the question. + example: Madrid answers: type: array items: type: string description: List with the possible answers. + example: Madrid, Paris, Roma o Londres (en diferentes items) createdAt: type: string format: date-time @@ -251,6 +276,7 @@ paths: type: string description: Error information. example: Internal Server Error + /history/users: get: summary: Get the history of all users. @@ -268,15 +294,19 @@ paths: username: type: string description: Username of the user. + example: student tpoints: type: integer description: Total points earned by the user. + example: 1000 ttime: type: integer description: Total time spent by the user (in seconds). + example: 330 ngames: type: integer description: Number of games played by the user. + example: 2 createdAt: type: string format: date-time diff --git a/users/userservice/openapi.yaml b/users/userservice/openapi.yaml index 8eb24c01..f8746215 100644 --- a/users/userservice/openapi.yaml +++ b/users/userservice/openapi.yaml @@ -39,6 +39,7 @@ paths: username: type: string description: User ID + example: student password: type: string description: Hashed password diff --git a/webapp/package-lock.json b/webapp/package-lock.json index 6be0e07f..76ce91e9 100644 --- a/webapp/package-lock.json +++ b/webapp/package-lock.json @@ -5,6 +5,7 @@ "requires": true, "packages": { "": { + "name": "webapp", "version": "0.1.0", "dependencies": { "@emotion/react": "^11.11.3", @@ -6594,8 +6595,6 @@ "version": "8.12.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", - "optional": true, - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", @@ -6610,9 +6609,7 @@ "node_modules/ajv-formats/node_modules/json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "optional": true, - "peer": true + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" }, "node_modules/ajv-keywords": { "version": "3.5.2", @@ -32642,13 +32639,14 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", - "requires": {}, + "requires": { + "ajv": "^8.0.0" + }, "dependencies": { "ajv": { - "version": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", - "optional": true, - "peer": true, "requires": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", @@ -32659,9 +32657,7 @@ "json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "optional": true, - "peer": true + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" } } }, diff --git a/webapp/src/components/AddUser.js b/webapp/src/components/AddUser.js index 4c51c6e7..42cd2294 100644 --- a/webapp/src/components/AddUser.js +++ b/webapp/src/components/AddUser.js @@ -20,6 +20,8 @@ const AddUser = () => { const addUser = async () => { if (name.trim() === '' || surname.trim() === '' || username.trim() === '' || password.trim() === '' || confirmPassword.trim() === '') { setError('Todos los campos deben de estar rellenos.'); + } else if (password.length < 8){ + setError('Las contraseñas deben contener más de 8 caracteres.'); } else if(password !== confirmPassword){ setError('Las contraseñas no coinciden.'); } else { diff --git a/webapp/src/components/AddUser.test.js b/webapp/src/components/AddUser.test.js index a0e56e4e..d43e1ad2 100644 --- a/webapp/src/components/AddUser.test.js +++ b/webapp/src/components/AddUser.test.js @@ -28,24 +28,23 @@ describe('AddUser component', () => { fireEvent.change(nameInput, { target: { value: 'testUsera' } }); fireEvent.change(surnameInput, { target: { value: 'testUsera' } }); fireEvent.change(usernameInput, { target: { value: 'testUsera' } }); - fireEvent.change(passwordInput, { target: { value: 'testPassword' } }); - fireEvent.change(confirmPasswordInput, { target: { value: 'testPassword' } }); + fireEvent.change(passwordInput, { target: { value: 'testP' } }); + fireEvent.change(confirmPasswordInput, { target: { value: 'testP' } }); - // Trigger the add user button click + // Probamos a poner una contraseña corta fireEvent.click(addUserButton); - - // Wait for the Snackbar to be open await waitFor(() => { - expect(screen.getByText(/Usuario añadido correctamente/i)).toBeInTheDocument(); + expect(screen.getByText(/Las contraseñas deben contener más de 8 caracteres./i)).toBeInTheDocument(); }); - /* + //Modificamos la contraseña para que contenga al menos de 8 caracteres + fireEvent.change(passwordInput, { target: { value: 'testPassw' } }); + fireEvent.change(confirmPasswordInput, { target: { value: 'testPassw' } }); fireEvent.click(addUserButton); - // Wait for the Snackbar to be open await waitFor(() => { - expect(screen.getByText(/Usuario ya registrado./i)).toBeInTheDocument(); + expect(screen.getByText(/Usuario añadido correctamente/i)).toBeInTheDocument(); }); - */ + }); it('try to add user but not introduce name', async () => { diff --git a/webapp/src/components/FirstGame.js b/webapp/src/components/FirstGame.js index ffcb5e46..02484a8a 100644 --- a/webapp/src/components/FirstGame.js +++ b/webapp/src/components/FirstGame.js @@ -2,25 +2,23 @@ import React, { useEffect, useState } from 'react'; import { Container, Typography, Box, LinearProgress} from '@mui/material'; import './FirstGame.css'; import 'react-circular-progressbar/dist/styles.css'; -import axios from 'axios'; import { useLocation, useNavigate } from 'react-router-dom'; import Button from './Button'; import { Footer } from './footer/Footer'; import { Nav } from './nav/Nav'; +import { gameStore } from './Util'; let currentQuestionIndex = 0; let haveFailedQuestion = false; -const apiEndpoint = process.env.REACT_APP_API_ENDPOINT|| 'http://localhost:8000'; let isCorrect = false let questions = []; let points = 0; let load = true; const previousBackgroundColor = '#1a1a1a' - const Quiz = () => { - + let username = localStorage.getItem("username") const navigator = useNavigate(); let allQuestions = useLocation().state.questions; let haveEnter = false; @@ -37,9 +35,7 @@ const Quiz = () => { setRemTime((progress) => { if(progress === 100){ checkAnswer(-1); - console.log("Antes", totalTime) setTotalTime(totalTime + progress/10) - console.log("Despues", totalTime) return 0; } const diff = 4; @@ -57,36 +53,18 @@ const Quiz = () => { }; function changeButtons(param) { - var borders = document.getElementsByClassName("border");; - for(var i = 0; i < Math.min(borders.length, allQuestions[0].options.length); i++) { + let borders = document.getElementsByClassName("border");; + for(let i = 0; i < Math.min(borders.length, allQuestions[0].options.length); i++) { borders[i].setAttribute("data-disabled", param) } } - const gameStore = async () => { - try { - var username = localStorage.getItem("username") - console.log(username) - console.log(questions) - console.log(totalTime) - var avgtime = totalTime/questions.length - console.log(avgtime) - const response = await axios.post(`${apiEndpoint}/storeGame`, { id, username, points, questions, avgtime}); - questions = [] - points = 0 - console.log(response) - } catch (error) { - console.error(error) - } - } - const checkAnswer = async (option) => { if (haveEnter) { return } load = false - console.log("Todas las preguntas", allQuestions) isCorrect = (option === allQuestions[currentQuestionIndex].correctAnswer); changeButtons("true") @@ -103,7 +81,6 @@ const Quiz = () => { }) await esperar(2000) - console.log(option) currentQuestionIndex = (currentQuestionIndex + 1); botonCorrecta.style.backgroundColor = previousBackgroundColor changeButtons("false") @@ -111,11 +88,10 @@ const Quiz = () => { haveFailedQuestion = false; load = true haveEnter = false - console.log("Calbo") if (currentQuestionIndex === allQuestions.length ) { - console.log("Entramos aqui") - gameStore() + gameStore(id, username, points, questions, totalTime/questions.length) + points = 0 navigator('/menu') } return @@ -124,7 +100,6 @@ const Quiz = () => { const botonIncorrecta = document.getElementById('option-' + allQuestions[currentQuestionIndex].options.indexOf(option)) if (!isCorrect) { - console.log("Entramos en el correct") botonIncorrecta.style.backgroundColor = 'red' } else { points = points += 100; @@ -132,7 +107,7 @@ const Quiz = () => { // Pasar a la siguiente pregunta después de responder - var indexAnswers = [allQuestions[currentQuestionIndex].options.indexOf(option), numberAnswer] + let indexAnswers = [allQuestions[currentQuestionIndex].options.indexOf(option), numberAnswer] questions.push({ title: allQuestions[currentQuestionIndex].question, @@ -140,10 +115,10 @@ const Quiz = () => { ansIndex: indexAnswers } ) - await esperar(2000); // Espera 2000 milisegundos (2 segundos) - botonIncorrecta.style.backgroundColor = previousBackgroundColor - botonCorrecta.style.backgroundColor = previousBackgroundColor + + botonIncorrecta.style.backgroundColor = botonCorrecta.style.backgroundColor = previousBackgroundColor + if (allQuestions.length-1 !== currentQuestionIndex) { currentQuestionIndex = (currentQuestionIndex + 1); } else { @@ -151,17 +126,15 @@ const Quiz = () => { } isCorrect = (false) - console.log("Durante antes", totalTime) await setTotalTime(totalTime + remTime/10) - console.log("Durante despues", totalTime) setRemTime(0) load = true changeButtons("false") - console.log(haveFailedQuestion) if(haveFailedQuestion) { - console.log("Entramos a guardar el juego") - await gameStore() + await gameStore(id, username, points, questions, totalTime/questions.length) + questions = [] + points = 0 haveFailedQuestion = false; currentQuestionIndex += 1 navigator('/menu') diff --git a/webapp/src/components/FirstGame.test.js b/webapp/src/components/FirstGame.test.js index 135a2484..6e2c559a 100644 --- a/webapp/src/components/FirstGame.test.js +++ b/webapp/src/components/FirstGame.test.js @@ -1,38 +1,78 @@ -import { render, screen, fireEvent } from '@testing-library/react'; -import { ContextFun } from './Context'; -import axios from 'axios'; -import MockAdapter from 'axios-mock-adapter'; +import React from 'react'; +import { act, render, fireEvent, waitFor } from '@testing-library/react'; +import Quiz from './FirstGame'; // Asegúrate de importar correctamente tu componente Quiz import { BrowserRouter as Router } from 'react-router-dom'; -import FirstGame from './FirstGame'; -import GameConfiguration from './game/GameConfiguration'; - -const mockAxios = new MockAdapter(axios); - -describe("First game component", () => { - - beforeEach(() => { - mockAxios.reset(); - }); - - test("one question -> 4 possible answers",async () => { - render( - - - - - - ); - - //Marcar la primera temática - let topic1 = document.getElementById('t0'); - fireEvent.click(topic1); - expect(topic1).toBeChecked(); - //Iniciamos el juego - let bt = screen.getByText('Comenzar Juego'); - fireEvent.click(bt); - //comprobamos que haya 2 botones para responder - const gamesBT = document.getElementsByClassName('allAnswers'); - //expect(gamesBT).toHaveLength(2); - }); - -}); \ No newline at end of file +import { ContextFun } from './Context'; + +beforeEach(() => { + +}); + +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useLocation: () => ({ + state: { + questions: [ + { + question: 'What is 2 + 2?', + options: ['3', '4', '5', '6'], + correctAnswer: '4', + }, + { + question: 'What is 2 + 2?', + options: ['3', '4', '5', '6'], + correctAnswer: '4', + } + ], + gameId: '123456', + } + }) +})); + +const questions = [ + { + question: 'What is 2 + 2?', + options: ['3', '4', '5', '6'], + correctAnswer: '4', + }, + { + question: 'What is 2 + 2?', + options: ['3', '4', '5', '6'], + correctAnswer: '4', + } +]; + +const state = { + questions: questions, + gameId: '123456', +}; + +describe('Quiz Component', () => { + jest.setTimeout(13000); + it('selects the correct answer', async () => { + const { getByText, getByTestId } = render( + + + + , { initialState: { state } }); + + // Wait for questions to load + await waitFor(() => getByText('What is 2 + 2?')); + + // Select the correct answer and check if it's green + fireEvent.click(getByText('4')); + expect(getByText('4')).toHaveStyle('background-color: green'); + await new Promise((r) => setTimeout(r, 2000)); + expect(getByText('4')).toHaveStyle('background-color: #1a1a1a') + + await waitFor(() => getByText('What is 2 + 2?')); + + // Select the correct answer and check if it's green + fireEvent.click(getByText('5')); + expect(getByText('5')).toHaveStyle('background-color: red'); + await new Promise((r) => setTimeout(r, 2000)); + expect(getByText('5')).toHaveStyle('background-color: #1a1a1a') + }); + +}); + diff --git a/webapp/src/components/Util.js b/webapp/src/components/Util.js index 691dd559..4b18bb9b 100644 --- a/webapp/src/components/Util.js +++ b/webapp/src/components/Util.js @@ -21,9 +21,7 @@ function shuffleArray(array) { } function secureRandomNumber(max) { - const randomBytes = new Uint32Array(1); - window.crypto.getRandomValues(randomBytes); - return randomBytes[0] % max; + return Math.floor(Math.random() * max); } const esperar = (ms) => { @@ -40,4 +38,15 @@ const generateGameId = async () => { } } -export {esperar, shuffleArray, secureRandomNumber, generateGameId} \ No newline at end of file +async function gameStore(id, username, points, questions, avgtime) { + try { + + const apiEndpoint = process.env.REACT_APP_API_ENDPOINT|| 'http://localhost:8000'; + await axios.post(`${apiEndpoint}/storeGame`, { id, username, points, questions, avgtime}); + questions = [] + } catch (error) { + console.error(error) + } +} + +export {esperar, shuffleArray, secureRandomNumber, generateGameId, gameStore} \ No newline at end of file diff --git a/webapp/src/components/Util.test.js b/webapp/src/components/Util.test.js index aa90b008..1313592c 100644 --- a/webapp/src/components/Util.test.js +++ b/webapp/src/components/Util.test.js @@ -5,13 +5,7 @@ jest.mock('axios'); describe('shuffleArray function', () => { // Mocking window.crypto.getRandomValues beforeEach(() => { - global.crypto = { - getRandomValues: jest.fn().mockImplementation((array) => { - for (let i = 0; i < array.length; i++) { - array[i] = i; - } - }), - }; + }); afterEach(() => { diff --git a/webapp/src/components/game/Calculator.js b/webapp/src/components/game/Calculator.js index 3b582cf5..249a40d0 100644 --- a/webapp/src/components/game/Calculator.js +++ b/webapp/src/components/game/Calculator.js @@ -1,26 +1,25 @@ import React from 'react'; -import { shuffleArray, secureRandomNumber, generateGameId,esperar } from '../Util'; +import { shuffleArray, secureRandomNumber, generateGameId, esperar, gameStore} from '../Util'; import { Container, Typography, Box, LinearProgress } from '@mui/material'; import { Footer } from '../footer/Footer'; import { Nav } from '../nav/Nav'; import Button from '../Button'; import { useState, useEffect} from 'react'; import { useNavigate } from 'react-router-dom'; -import axios from 'axios'; let questions = []; let load = true; const previousBackgroundColor = '#1a1a1a'; let points = 0; -const apiEndpoint = process.env.REACT_APP_API_ENDPOINT|| 'http://localhost:8000'; let answeredQuestions = []; -let savedGame = false; const Calculator = () => { //let questionIndex = -1 + let username = localStorage.getItem("username") const [questionIndex, setQuestionIndex] = useState(0); + const id = generateGameId(); const navigator = useNavigate(); @@ -34,12 +33,10 @@ const Calculator = () => { const time = setInterval(() => { setRemTime((progress) => { if(progress === 100){ - console.log("Antes", totalTime) setTotalTime(totalTime + progress/10) - console.log("Despues", totalTime) - - gameStore(); - + gameStore(id, username, points, answeredQuestions, totalTime/answeredQuestions.length); + init(); + navigator('/menu') return 0; } const diff = 0.5; @@ -52,28 +49,6 @@ const Calculator = () => { }; }); - const gameStore = async () => { - try { - var username = localStorage.getItem("username") - console.log(username) - console.log(answeredQuestions) - console.log(totalTime) - var avgtime = totalTime/answeredQuestions.length - console.log(avgtime) - const id = await generateGameId(); - if(!savedGame){ - savedGame = true; - const response = await axios.post(`${apiEndpoint}/storeGame`, { id, username, points, questions: answeredQuestions, avgtime}); - console.log(response) - } - } catch (error) { - console.error(error) - } finally { - init(); - navigator('/menu') - } - } - function init(){ questions = []; points = 0; @@ -84,10 +59,6 @@ const Calculator = () => { let num1 = secureRandomNumber(10) + 1; let num2 = secureRandomNumber(10) + 1; - console.log(questionIndex) - - num2 = secureRandomNumber(10) + 1; - const operator = ['+', '-', 'x', '÷'][secureRandomNumber(3)]; let correctAnswer; @@ -104,8 +75,7 @@ const Calculator = () => { case '÷': correctAnswer = Math.round(num1 / num2); break; - default: - break; + default: break; } const option = [correctAnswer]; @@ -133,16 +103,10 @@ const Calculator = () => { const numberAnswer = questions[questionIndex].options.indexOf(questions[questionIndex].correctAnswer); const choiceNumber = questions[questionIndex].options.indexOf(selectedAnswer); - console.log(numberAnswer) - console.log(choiceNumber) - - //console.log(numberAnswer) const botonCorrecta = document.getElementById('option-' + numberAnswer); let botonIncorrecta = null; botonCorrecta.style.backgroundColor = 'green'; - /*title: allQuestions[currentQuestionIndex].question, - answers: allQuestions[currentQuestionIndex].options, - ansIndex: indexAnswers*/ + storeQuestion(questions[questionIndex].q, questions[questionIndex].options, [choiceNumber, numberAnswer]); if (selectedAnswer !== questions[questionIndex].correctAnswer) { botonIncorrecta = document.getElementById('option-' + questions[questionIndex].options.indexOf(selectedAnswer)); @@ -183,8 +147,7 @@ const Calculator = () => {
- - + {questions[questionIndex].q} diff --git a/webapp/src/components/game/Calculator.test.js b/webapp/src/components/game/Calculator.test.js index bcf5ae3a..991ae3b9 100644 --- a/webapp/src/components/game/Calculator.test.js +++ b/webapp/src/components/game/Calculator.test.js @@ -5,19 +5,10 @@ import { BrowserRouter as Router, useLocation } from 'react-router-dom'; import Calculator from './Calculator'; describe('Calculator Component', () => { - beforeEach(() => { - global.crypto = { - getRandomValues: jest.fn().mockImplementation((array) => { - for (let i = 0; i < array.length; i++) { - array[i] = i; - } - }), - }; - }); - + jest.setTimeout(10000); + test("renders Calculator",async () => { - - /*render( + render( @@ -26,26 +17,34 @@ describe('Calculator Component', () => { ); // Comprobamos que el número de elementos sea 3 - let operation = document.getElementsByClassName('questionStructure')[0][0]; + let operation = document.getElementById("questionText").textContent; const separatedText = operation.split(' '); - expect(separatedText.length).toBeGreaterThan(3); + expect(separatedText.length).toBe(3); + // Comprobamos que el número de respuestas posibles sea 4 - let answers = document.getElementsByClassName('questionStructure')[1]; + let answers = document.getElementsByClassName('allAnswers')[0].childNodes; expect(answers).toHaveLength(4); + // Tratamos de hacer la operación - let number1 = separatedText[0]; - let number2 = separatedText[2]; - let op = separatedText[1]; - let result; - switch (op) { - case '+': result = number1 + number2; break; - case '-': result = number1 - number2; break; - case 'x': result = number1 * number2; break; - case '÷': result = Math.round(number1 / number2); break; - } - expect(screen.getByText(result)).toBeInTheDocument();*/ - + //for(let i = 0; i < 2; i++){ + let number1 = parseInt(separatedText[0]); + let number2 = parseInt(separatedText[2]); + let op = separatedText[1]; + let result; + switch (op) { + case '+': result = number1 + number2; break; + case '-': result = number1 - number2; break; + case 'x': result = number1 * number2; break; + case '÷': result = Math.round(number1 / number2); break; + } + let bt = screen.getByText(result); + expect(bt).toBeInTheDocument(); + bt.click(); + expect(window.getComputedStyle(bt).getPropertyValue('background-color')).toBe("green"); + + await new Promise(resolve => setTimeout(resolve, 3000)); + expect(window.getComputedStyle(bt).getPropertyValue('background-color')).not.toBe("green"); + //} }); - -}); +}); \ No newline at end of file diff --git a/webapp/src/help/Help.jsx b/webapp/src/help/Help.jsx index d5930aa6..092aa333 100644 --- a/webapp/src/help/Help.jsx +++ b/webapp/src/help/Help.jsx @@ -2,33 +2,59 @@ import { Nav } from "../components/nav/Nav"; import { Footer } from "../components/footer/Footer"; function Help(){ - return (<> -