diff --git a/questionservice/package-lock.json b/questionservice/package-lock.json index 49ba9a7..35c27e0 100644 --- a/questionservice/package-lock.json +++ b/questionservice/package-lock.json @@ -7,7 +7,6 @@ "dependencies": { "axios": "^1.6.8", "express": "^4.18.3", - "mongoose": "^8.3.0", "wikibase-sdk": "^8.1.1" }, "devDependencies": { @@ -977,14 +976,6 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@mongodb-js/saslprep": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.5.tgz", - "integrity": "sha512-XLNOMH66KhJzUJNwT/qlMnS4WsNDWD5ASdyaSH3EtK+F4r/CFGa3jT4GNi4mfOitGvWXtdLgQJkQjxSVrio+jA==", - "dependencies": { - "sparse-bitfield": "^3.0.3" - } - }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -1098,19 +1089,6 @@ "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", "dev": true }, - "node_modules/@types/webidl-conversions": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", - "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==" - }, - "node_modules/@types/whatwg-url": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.4.tgz", - "integrity": "sha512-lXCmTWSHJvf0TRSO58nm978b8HJ/EdsSsEKLd3ODHFjo+3VGAyyTp4v50nWvwtzBxSMQrVOK7tcuN0zGPLICMw==", - "dependencies": { - "@types/webidl-conversions": "*" - } - }, "node_modules/@types/yargs": { "version": "17.0.32", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", @@ -1424,14 +1402,6 @@ "node-int64": "^0.4.0" } }, - "node_modules/bson": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/bson/-/bson-6.6.0.tgz", - "integrity": "sha512-BVINv2SgcMjL4oYbBuCQTpE3/VKOSxrOA8Cj/wQP7izSzlBGVomdm+TcUd0Pzy0ytLSSDweCKQ6X3f5veM5LQA==", - "engines": { - "node": ">=16.20.1" - } - }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -3218,14 +3188,6 @@ "node": ">=6" } }, - "node_modules/kareem": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.6.0.tgz", - "integrity": "sha512-B9wwgyKKKZkxYZXQzefvb/Ykh9eHixxR+ttTP2c/Pq8NvHi1iYIAImf3nj/DXkPcnenjGEffhPWXnCFRIbNAhw==", - "engines": { - "node": ">=12.0.0" - } - }, "node_modules/kleur": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", @@ -3336,11 +3298,6 @@ "node": ">= 0.6" } }, - "node_modules/memory-pager": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", - "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==" - }, "node_modules/merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", @@ -3424,126 +3381,6 @@ "node": "*" } }, - "node_modules/mongodb": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.5.0.tgz", - "integrity": "sha512-Fozq68InT+JKABGLqctgtb8P56pRrJFkbhW0ux+x1mdHeyinor8oNzJqwLjV/t5X5nJGfTlluxfyMnOXNggIUA==", - "dependencies": { - "@mongodb-js/saslprep": "^1.1.5", - "bson": "^6.4.0", - "mongodb-connection-string-url": "^3.0.0" - }, - "engines": { - "node": ">=16.20.1" - }, - "peerDependencies": { - "@aws-sdk/credential-providers": "^3.188.0", - "@mongodb-js/zstd": "^1.1.0", - "gcp-metadata": "^5.2.0", - "kerberos": "^2.0.1", - "mongodb-client-encryption": ">=6.0.0 <7", - "snappy": "^7.2.2", - "socks": "^2.7.1" - }, - "peerDependenciesMeta": { - "@aws-sdk/credential-providers": { - "optional": true - }, - "@mongodb-js/zstd": { - "optional": true - }, - "gcp-metadata": { - "optional": true - }, - "kerberos": { - "optional": true - }, - "mongodb-client-encryption": { - "optional": true - }, - "snappy": { - "optional": true - }, - "socks": { - "optional": true - } - } - }, - "node_modules/mongodb-connection-string-url": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.0.tgz", - "integrity": "sha512-t1Vf+m1I5hC2M5RJx/7AtxgABy1cZmIPQRMXw+gEIPn/cZNF3Oiy+l0UIypUwVB5trcWHq3crg2g3uAR9aAwsQ==", - "dependencies": { - "@types/whatwg-url": "^11.0.2", - "whatwg-url": "^13.0.0" - } - }, - "node_modules/mongoose": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.3.0.tgz", - "integrity": "sha512-Y5QNnuA38CEin8hnA+q//nUVztIi4Xklu9xlmbkd1KdWHnIlemSwf5IL/evcI+e2zplL4g5Y6PMkO+nPSAnIdA==", - "dependencies": { - "bson": "^6.5.0", - "kareem": "2.6.0", - "mongodb": "6.5.0", - "mpath": "0.9.0", - "mquery": "5.0.0", - "ms": "2.1.3", - "sift": "16.0.1" - }, - "engines": { - "node": ">=16.20.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mongoose" - } - }, - "node_modules/mongoose/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "node_modules/mpath": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz", - "integrity": "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/mquery": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/mquery/-/mquery-5.0.0.tgz", - "integrity": "sha512-iQMncpmEK8R8ncT8HJGsGc9Dsp8xcgYMVSbs5jgnm1lFHTZqMJTUWTDx1LBO8+mK3tPNZWFLBghQEIOULSTHZg==", - "dependencies": { - "debug": "4.x" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/mquery/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/mquery/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, "node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -3849,14 +3686,6 @@ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "engines": { - "node": ">=6" - } - }, "node_modules/pure-rand": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", @@ -4105,11 +3934,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/sift": { - "version": "16.0.1", - "resolved": "https://registry.npmjs.org/sift/-/sift-16.0.1.tgz", - "integrity": "sha512-Wv6BjQ5zbhW7VFefWusVP33T/EM0vYikCaQ2qR8yULbsilAT8/wQaXvuQ3ptGLpoKx+lihJE3y2UTgKDyyNHZQ==" - }, "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", @@ -4150,14 +3974,6 @@ "source-map": "^0.6.0" } }, - "node_modules/sparse-bitfield": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", - "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", - "dependencies": { - "memory-pager": "^1.0.2" - } - }, "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -4428,17 +4244,6 @@ "node": ">=0.6" } }, - "node_modules/tr46": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-4.1.1.tgz", - "integrity": "sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==", - "dependencies": { - "punycode": "^2.3.0" - }, - "engines": { - "node": ">=14" - } - }, "node_modules/type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", @@ -4555,26 +4360,6 @@ "makeerror": "1.0.12" } }, - "node_modules/webidl-conversions": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", - "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", - "engines": { - "node": ">=12" - } - }, - "node_modules/whatwg-url": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-13.0.0.tgz", - "integrity": "sha512-9WWbymnqj57+XEuqADHrCJ2eSXzn8WXIW/YSGaZtb2WKAInQ6CHfaUUcTyyver0p8BDg5StLQq8h1vtZuwmOig==", - "dependencies": { - "tr46": "^4.1.1", - "webidl-conversions": "^7.0.0" - }, - "engines": { - "node": ">=16" - } - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/questionservice/question-service.js b/questionservice/question-service.js index 52f7588..a3213f7 100644 --- a/questionservice/question-service.js +++ b/questionservice/question-service.js @@ -32,7 +32,7 @@ app.use(express.json()); var imgToAssociatedMap = new Map() var answerToQuestionMap = new Map() -class WIQ_API{ +class WIQ_API { /** * Extracts from wikidata images and their associates, then selects 4 images and one of * their associates for the question so the question is constructed with it as the target @@ -48,12 +48,12 @@ class WIQ_API{ async getQuestionAndImages(query, imgTypeName, relation) { //Num of fetched items - const itemsNum = 200 + const itemsNum = 200 //Required by wikidata to accept the request const headers = new Headers(); headers.append('User-Agent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)' - +' AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36'); + + ' AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36'); //Constructing the url for the wikidata request var url = wbk.sparqlQuery(query); @@ -73,24 +73,24 @@ class WIQ_API{ //I filter in case the label does not have a proper name //and just a wikidata identifier (Q followed by numbers) const regex = /Q\d*/ - while(regex.test(data.results.bindings[chosenNums[0]].itemLabel.value)){ - this.#getRandomNumNotInSetAndUpdate(itemsNum,chosenNums) + while (regex.test(data.results.bindings[chosenNums[0]].itemLabel.value)) { + this.#getRandomNumNotInSetAndUpdate(itemsNum, chosenNums) chosenNums[0] = chosenNums.pop() } finalChosenLabels.push(data.results.bindings[chosenNums[0]].itemLabel.value); - for(let i=0;i0){ + while (chosenNums.length > 0) { imgs.push(data.results.bindings[chosenNums.pop()].image.value) associates.push(finalChosenLabels.pop()) imgToAssociatedMap.set(imgs[counter], associates[counter]) @@ -98,27 +98,27 @@ class WIQ_API{ } //Choose a random item of the chosen to make the question - const chosenNum = this.#getRandomNumNotInSetAndUpdate(numOfChosen,chosenNums) + const chosenNum = this.#getRandomNumNotInSetAndUpdate(numOfChosen, chosenNums) const chosenAssociate = associates[chosenNum] let correctImg = imgs[chosenNum] const question = `Which of the following ${imgTypeName} ${relation} ${chosenAssociate}?` - answerToQuestionMap.set(correctImg,question) + answerToQuestionMap.set(correctImg, question) const questionAndImages = { question: question, - images: [`${imgs[0]}`,`${imgs[1]}`,`${imgs[2]}`,`${imgs[3]}`] + images: [`${imgs[0]}`, `${imgs[1]}`, `${imgs[2]}`, `${imgs[3]}`] } return JSON.stringify(questionAndImages) } - #getRandomNumNotInSetAndUpdate(numLimit, set){ + #getRandomNumNotInSetAndUpdate(numLimit, set) { let randomNumber; - do { - randomNumber = Math.floor(Math.random() * numLimit); - } while (set.includes(randomNumber)); // Ensure the number is unique - set.push(randomNumber); + do { + randomNumber = Math.floor(Math.random() * numLimit); + } while (set.includes(randomNumber)); // Ensure the number is unique + set.push(randomNumber); return randomNumber } } @@ -140,8 +140,8 @@ app.get('/imgs/flags/question', async (req, res) => { wdt:P41 ?image. } LIMIT 200` - const question = JSON.parse(await wiq.getQuestionAndImages(query,"flags","belongs to")); - res.json(question); + const question = JSON.parse(await wiq.getQuestionAndImages(query, "flags", "belongs to")); + res.json(question); //LOS STATUSS!!!!!!!!!!! }); /** @@ -158,8 +158,8 @@ app.get('/imgs/cities/question', async (req, res) => { ?item wdt:P18 ?image. } LIMIT 200` - const question = JSON.parse(await wiq.getQuestionAndImages(query,"images","corresponds to")); - res.json(question); + const question = JSON.parse(await wiq.getQuestionAndImages(query, "images", "corresponds to")); + res.json(question); //LOS STATUSS!!!!!!!!!!! }); /** @@ -176,8 +176,8 @@ app.get('/imgs/monuments/question', async (req, res) => { wdt:P18 ?image. } LIMIT 200` - const question = JSON.parse(await wiq.getQuestionAndImages(query,"images","corresponds to")); - res.json(question); + const question = JSON.parse(await wiq.getQuestionAndImages(query, "images", "corresponds to")); + res.json(question); //LOS STATUSS!!!!!!!!!!! }); /** @@ -194,8 +194,8 @@ app.get('/imgs/tourist_attractions/question', async (req, res) => { wdt:P18 ?image. } LIMIT 200` - const question = JSON.parse(await wiq.getQuestionAndImages(query,"images","corresponds to")); - res.json(question); + const question = JSON.parse(await wiq.getQuestionAndImages(query, "images", "corresponds to")); + res.json(question); //LOS STATUSS!!!!!!!!!!! }); /** @@ -205,6 +205,7 @@ app.get('/imgs/tourist_attractions/question', async (req, res) => { * @param {Object} res - Contains the question (question) and the foods (images) */ app.get('/imgs/foods/question', async (req, res) => { + //Gets food images and their associated names const query = `SELECT ?item ?itemLabel ?image WHERE { SERVICE wikibase:label { bd:serviceParam wikibase:language "[AUTO_LANGUAGE],en". } @@ -212,8 +213,8 @@ app.get('/imgs/foods/question', async (req, res) => { wdt:P18 ?image. } LIMIT 200` - const question = JSON.parse(await wiq.getQuestionAndImages(query,"images","corresponds to")); - res.json(question); + const question = JSON.parse(await wiq.getQuestionAndImages(query, "images", "corresponds to")); + res.json(question); //LOS STATUSS!!!!!!!!!!! }); @@ -233,22 +234,26 @@ function validateRequiredFields(req, requiredFields) { * associate will be returned as well */ app.post('/imgs/answer', async (req, res) => { - const obj = req.body; - - if(obj.question==answerToQuestionMap.get(obj.answer)){ - await axios.post(userServiceUrl+'/addpoints', - {username: obj.username, category: obj.category, correct: "true" } ); - res.status(200).json({ - correct: "true", - }) - } else { - await axios.post(userServiceUrl+'/addpoints', - {username: obj.username, category: obj.category, correct: "false" } ); - - res.status(200).json({ - correct: "false", - associate: `${imgToAssociatedMap.get(obj.answer)}` - }) + try { + const obj = req.body; + + if (obj.question == answerToQuestionMap.get(obj.answer)) { + await axios.post(userServiceUrl + '/addpoints', + { username: obj.username, category: obj.category, correct: "true" }); + res.status(200).json({ + correct: "true", + }) + } else { + await axios.post(userServiceUrl + '/addpoints', + { username: obj.username, category: obj.category, correct: "false" }); + + res.status(200).json({ + correct: "false", + associate: `${imgToAssociatedMap.get(obj.answer)}` + }) + } + } catch (e) { //SIEMPRE RODEAR CON TRY CATCH + res.status(500).json({ error: e.message }) } }); diff --git a/users/userservice/user-service.test.js b/users/userservice/user-service.test.js index 8dd8ea1..12d9166 100644 --- a/users/userservice/user-service.test.js +++ b/users/userservice/user-service.test.js @@ -8,18 +8,19 @@ beforeAll(async () => { mongoServer = await MongoMemoryServer.create(); const mongoUri = mongoServer.getUri(); process.env.MONGODB_URI = mongoUri; - app = require('./user-service'); + app = require('./user-service'); }); afterAll(async () => { - app.close(); - await mongoServer.stop(); + app.close(); + await mongoServer.stop(); }); describe('User Service', () => { it('should add a new user on POST /adduser', async () => { const newUser = { username: 'testuser', + email: 'test@test.com', password: 'testpassword', }; @@ -27,4 +28,72 @@ describe('User Service', () => { expect(response.status).toBe(200); expect(response.body).toHaveProperty('username', 'testuser'); }); + + it('should prevent creating a new user with wrong data on POST /adduser', async () => { + const newUser = { + + email: 'test@test.com', + password: 'testpassword', + }; + + const response = await request(app).post('/adduser').send(newUser); + expect(response.status).toBe(400); + }); + + it('should add a point to a user on POST /addpoints', async () => { + const points = { + username: 'testuser', + category: 'flags', + correct: 'true', + }; + const newUser = { + username: 'testuser', + email: 'test@test.com', + password: 'testpassword', + }; + + const newRequest = await request(app).post('/adduser').send(newUser); + const response = await request(app).post('/addpoints').send(points); + expect(response.status).toBe(200); + expect(response.body).toHaveProperty('username', 'testuser'); + }); + + it('should add a wrong question to a user on POST /addpoints', async () => { + const points = { + username: 'testuser', + category: 'flags', + correct: 'false', + }; + const newUser = { + username: 'testuser', + email: 'test@test.com', + password: 'testpassword', + }; + + const newRequest = await request(app).post('/adduser').send(newUser); + const response = await request(app).post('/addpoints').send(points); + expect(response.status).toBe(200); + expect(response.body).toHaveProperty('username', 'testuser'); + }); + + it('should prevent adding points to a user with wrong data on POST /addpoints', async () => { + const points = { + username: 'testuser', + category: 'fail', + }; + const newUser = { + username: 'testuser', + email: 'test@test.test', + password: 'testpassword', + }; + const newRequest = await request(app).post('/adduser').send(newUser); + const response = await request(app).post('/addpoints').send(points); + expect(response.status).toBe(400); + }); + + it("should show a user's ranking on a category GET /rankings/:filter", async () => { + const response = await request(app).get('/rankings/flags'); + expect(response.status).toBe(200); + expect(response.body); + }); }); diff --git a/webapp/src/App.test.js b/webapp/src/App.test.js index 5e3b731..b3ff564 100644 --- a/webapp/src/App.test.js +++ b/webapp/src/App.test.js @@ -1,8 +1,27 @@ import { render, screen } from '@testing-library/react'; import App from './App'; +import AuthProvider from 'react-auth-kit/AuthProvider'; +import createStore from 'react-auth-kit/createStore'; -test('renders learn react link', () => { - render(); - const linkElement = screen.getByText(/Welcome to the 2024 edition of the Software Architecture course/i); + +test('renders /', () => { + const store = createStore({ + authName: '_auth', + authType: 'cookie', + cookieDomain: window.location.hostname, + cookieSecure: window.location.protocol === 'https:', + }); + + render( + + + + + ); + const linkElement = screen.getByText(/Welcome to WIQ, Please log in to play!/i); expect(linkElement).toBeInTheDocument(); + const button = screen.getByRole('button', { name: /Create account/i }); + expect(button).toBeInTheDocument(); }); diff --git a/webapp/src/components/AddUser.jsx b/webapp/src/components/AddUser.jsx index 09dcdba..68e0e44 100644 --- a/webapp/src/components/AddUser.jsx +++ b/webapp/src/components/AddUser.jsx @@ -25,7 +25,12 @@ const AddUser = () => { setOpenSnackbar(true); navigate('/login'); } catch (error) { - setError(error.response.data.error); + if(error.response===undefined){ + setError("Error: There was a problem..."); + } + else{ + setError(error.response.data.error); + } } }; diff --git a/webapp/src/components/AddUser.test.js b/webapp/src/components/AddUser.test.js index 8733488..e6e6817 100644 --- a/webapp/src/components/AddUser.test.js +++ b/webapp/src/components/AddUser.test.js @@ -1,22 +1,45 @@ import React from 'react'; -import { render, fireEvent, screen, waitFor } from '@testing-library/react'; +import { fireEvent, screen, waitFor } from '@testing-library/react'; import axios from 'axios'; import MockAdapter from 'axios-mock-adapter'; import AddUser from './AddUser'; +import { render } from '@testing-library/react'; +import { BrowserRouter } from 'react-router-dom'; + + + const mockAxios = new MockAdapter(axios); + describe('AddUser component', () => { beforeEach(() => { mockAxios.reset(); }); + it('renders correctly', () => { + render( + + + ); + + expect(screen.getByLabelText('Username')).toBeInTheDocument(); + expect(screen.getByLabelText('Email')).toBeInTheDocument(); + expect(screen.getByLabelText('Password')).toBeInTheDocument(); + expect(screen.getByLabelText('Confirm Password')).toBeInTheDocument(); + }); + it('should add user successfully', async () => { - render(); + render( + + ); + const usernameInput = screen.getByLabelText(/Username/i); - const passwordInput = screen.getByLabelText(/Password/i); - const addUserButton = screen.getByRole('button', { name: /Add User/i }); + const emailInput = screen.getByLabelText(/Email/i); + const passwordInput = screen.getByLabelText("Password"); + const cpasswordInput = screen.getByLabelText(/Confirm Password/i); + const addUserButton = screen.getByRole('button', { name: /Register/i }); // Mock the axios.post request to simulate a successful response mockAxios.onPost('http://localhost:8000/adduser').reply(200); @@ -24,6 +47,8 @@ describe('AddUser component', () => { // Simulate user input fireEvent.change(usernameInput, { target: { value: 'testUser' } }); fireEvent.change(passwordInput, { target: { value: 'testPassword' } }); + fireEvent.change(cpasswordInput, { target: { value: 'testPassword' } }); + fireEvent.change(emailInput, { target: { value: 'test@test.com' } }); // Trigger the add user button click fireEvent.click(addUserButton); @@ -34,12 +59,40 @@ describe('AddUser component', () => { }); }); + it('should handle wrong passwords when adding user', async () => { + render( + + ); + + const usernameInput = screen.getByLabelText(/Username/i); + const passwordInput = screen.getByLabelText("Password"); + const addUserButton = screen.getByRole('button', { name: /Register/i }); + + // Mock the axios.post request to simulate an error response + mockAxios.onPost('http://localhost:8000/adduser').reply(500, { error: 'Internal Server Error' }); + + // Simulate user input + fireEvent.change(usernameInput, { target: { value: 'testUser' } }); + fireEvent.change(passwordInput, { target: { value: 'testPassword' } }); + fireEvent.change(passwordInput, { target: { value: 'testPassword' } }); + + // Trigger the add user button click + fireEvent.click(addUserButton); + + // Wait for the error Snackbar to be open + await waitFor(() => { + expect(screen.getByText("Error: Passwords do not match")).toBeInTheDocument(); + }); + }); + it('should handle error when adding user', async () => { - render(); + render( + + ); const usernameInput = screen.getByLabelText(/Username/i); - const passwordInput = screen.getByLabelText(/Password/i); - const addUserButton = screen.getByRole('button', { name: /Add User/i }); + const passwordInput = screen.getByLabelText("Password"); + const addUserButton = screen.getByRole('button', { name: /Register/i }); // Mock the axios.post request to simulate an error response mockAxios.onPost('http://localhost:8000/adduser').reply(500, { error: 'Internal Server Error' }); @@ -53,7 +106,7 @@ describe('AddUser component', () => { // Wait for the error Snackbar to be open await waitFor(() => { - expect(screen.getByText(/Error: Internal Server Error/i)).toBeInTheDocument(); + expect(screen.getByText("Error: Passwords do not match")).toBeInTheDocument(); }); }); }); diff --git a/webapp/src/components/Login.jsx b/webapp/src/components/Login.jsx index 433d0ea..4c73cbe 100644 --- a/webapp/src/components/Login.jsx +++ b/webapp/src/components/Login.jsx @@ -18,6 +18,7 @@ const Login = () => { const loginUser = async () => { try { const response = await axios.post(`${apiEndpoint}/login`, { username, password }).then((res) => { + if (res.status === 200) { if (signIn({ auth: { @@ -35,14 +36,22 @@ const Login = () => { navigate('/'); } else { //Throw error + throw new Error('Error while signing in'); } + } }); setOpenSnackbar(true); } catch (error) { - setError(error.response.data.error); + if(error.response===undefined){ + setError("Error: There was a problem..."); + } + else{ + setError(error.response.data.error); + } + } }; diff --git a/webapp/src/components/Login.test.js b/webapp/src/components/Login.test.js index af102dc..af02d63 100644 --- a/webapp/src/components/Login.test.js +++ b/webapp/src/components/Login.test.js @@ -3,7 +3,9 @@ import { render, fireEvent, screen, waitFor, act } from '@testing-library/react' import axios from 'axios'; import MockAdapter from 'axios-mock-adapter'; import Login from './Login'; - +import AuthProvider from 'react-auth-kit/AuthProvider'; +import { BrowserRouter } from 'react-router-dom'; +import createStore from 'react-auth-kit/createStore'; const mockAxios = new MockAdapter(axios); describe('Login component', () => { @@ -12,29 +14,62 @@ describe('Login component', () => { }); it('should log in successfully', async () => { - render(); + const store = createStore({ + authName: '_auth', + authType: 'cookie', + cookieDomain: window.location.hostname, + cookieSecure: window.location.protocol === 'https:', + }); + render( + + + + + ); + const usernameInput = screen.getByLabelText(/Username/i); const passwordInput = screen.getByLabelText(/Password/i); const loginButton = screen.getByRole('button', { name: /Login/i }); - + const mock = jest.fn(); + jest.mock('react-router-dom', () => ({ + useNavigate: () => mock, + })); // Mock the axios.post request to simulate a successful response - mockAxios.onPost('http://localhost:8000/login').reply(200, { createdAt: '2024-01-01T12:34:56Z' }); + mockAxios.onPost('http://localhost:8000/login').reply(200, { username:"testUser",email:"test@test.com",createdAt: '2024-01-01T12:34:56Z',token: 'testToken'}); // Simulate user input await act(async () => { - fireEvent.change(usernameInput, { target: { value: 'testUser' } }); - fireEvent.change(passwordInput, { target: { value: 'testPassword' } }); - fireEvent.click(loginButton); - }); - - // Verify that the user information is displayed - expect(screen.getByText(/Hello testUser!/i)).toBeInTheDocument(); - expect(screen.getByText(/Your account was created on 1\/1\/2024/i)).toBeInTheDocument(); + fireEvent.change(usernameInput, { target: { value: 'testUser' } }); + fireEvent.change(passwordInput, { target: { value: 'testPassword' } }); + fireEvent.click(loginButton); + }); + + + const linkElement = screen.getByText(/Error: Error: There was a problem.../i); + expect(linkElement).toBeInTheDocument(); + + }); it('should handle error when logging in', async () => { - render(); + const store = createStore({ + authName: '_auth', + authType: 'cookie', + cookieDomain: window.location.hostname, + cookieSecure: window.location.protocol === 'https:', + }); + + render( + + + + + ); const usernameInput = screen.getByLabelText(/Username/i); const passwordInput = screen.getByLabelText(/Password/i); @@ -55,8 +90,7 @@ describe('Login component', () => { expect(screen.getByText(/Error: Unauthorized/i)).toBeInTheDocument(); }); - // Verify that the user information is not displayed - expect(screen.queryByText(/Hello testUser!/i)).toBeNull(); - expect(screen.queryByText(/Your account was created on/i)).toBeNull(); }); + + }); diff --git a/webapp/src/components/MainPage.jsx b/webapp/src/components/MainPage.jsx index 435a356..e4ea156 100644 --- a/webapp/src/components/MainPage.jsx +++ b/webapp/src/components/MainPage.jsx @@ -26,13 +26,13 @@ const MainPage = () => {

Welcome back, {auth.username}!


-
:

Welcome to WIQ, Please log in to play!

-
diff --git a/webapp/src/components/MainPage.test.js b/webapp/src/components/MainPage.test.js new file mode 100644 index 0000000..f91691e --- /dev/null +++ b/webapp/src/components/MainPage.test.js @@ -0,0 +1,30 @@ +import React from 'react'; +import { render, fireEvent } from '@testing-library/react'; +import { jest } from '@jest/globals'; +import MainPage from './MainPage'; +import useIsAuthenticated from 'react-auth-kit/hooks/useIsAuthenticated'; +import useAuthUser from 'react-auth-kit/hooks/useAuthUser'; + + +jest.mock('react-auth-kit/hooks/useIsAuthenticated'); +jest.mock('react-auth-kit/hooks/useAuthUser'); +const mock = jest.fn(); +jest.mock('react-router-dom', () => ({ + useNavigate: () => mock, +})); +describe('MainPage', () => { + it('renders welcome message for authenticated user', () => { + useIsAuthenticated.mockReturnValue(() => true); + useAuthUser.mockReturnValue({ username: 'testUser' }); + const { getByText } = render(); + expect(getByText('Welcome back, testUser!')).toBeInTheDocument(); + }); + + it('renders welcome message for unauthenticated user', () => { + useIsAuthenticated.mockReturnValue(() => false); + const { getByText } = render(); + expect(getByText('Welcome to WIQ, Please log in to play!')).toBeInTheDocument(); + }); + + +}); \ No newline at end of file