diff --git a/gatewayservice/gateway-service.test.js b/gatewayservice/gateway-service.test.js index 0517d12..a98da05 100644 --- a/gatewayservice/gateway-service.test.js +++ b/gatewayservice/gateway-service.test.js @@ -235,4 +235,158 @@ describe('Gateway Service without mocked micro services', () => { expect(error.response.data.error).toBe('Internal server error'); } }); + + it('should not forward login request and give 400', async () => { + axios.post.mockImplementation((url, data) => { + if (url.endsWith('/login')) { + return Promise.reject({ + response: { + status: 400, + data: { message: 'Invalid username or password' }, + }, + }); + } + }); + + try{ + await request(app) + .post('/login') + .send({ username: 'testuser', password: 'testpassword' }); + + } catch(error){ + expect(error.response.status).toBe(400); + expect(error.response.data.error).toBe('Invalid username or password'); + } + }); + + it('should not forward adduser request and give 500', async () => { + try{ + await request(app) + .post('/adduser') + .send({ username: 'testuser', password: 'testpassword' }); + + } catch(error){ + expect(error.response.status).toBe(500); + expect(error.response.data.error).toBe('Internal server error'); + } + }); + + it('should not forward forgetPassword request and give 500', async () => { + try{ + await request(app) + .post('/forgetPassword'); + + } catch(error){ + expect(error.response.status).toBe(500); + expect(error.response.data.error).toBe('Internal server error'); + } + }); + + it('should not forward changePassword request and give 500', async () => { + try{ + await request(app) + .post('/changePassword') + .set('token', 'valorDelToken');; + + } catch(error){ + expect(error.response.status).toBe(500); + expect(error.response.data.error).toBe('Internal server error'); + } + }); + + it('should not forward questions request and give 500', async () => { + try{ + await request(app) + .get('/questions') + .set('token', 'valorDelToken');; + + } catch(error){ + expect(error.response.status).toBe(500); + expect(error.response.data.error).toBe('Internal server error'); + } + }); + + it('should not forward questions request and give 500', async () => { + try{ + await request(app) + .get('/questions/es/1/CAPITAL') + .set('token', 'valorDelToken');; + + } catch(error){ + expect(error.response.status).toBe(500); + expect(error.response.data.error).toBe('Internal server error'); + } + }); + + it('should not forward questions request and give 500', async () => { + try{ + await request(app) + .get('/questions/es/1') + .set('token', 'valorDelToken');; + + } catch(error){ + expect(error.response.status).toBe(500); + expect(error.response.data.error).toBe('Internal server error'); + } + }); + + it('should not forward questions request and give 500', async () => { + try{ + await request(app) + .get('/questions/es') + .set('token', 'valorDelToken');; + + } catch(error){ + expect(error.response.status).toBe(500); + expect(error.response.data.error).toBe('Internal server error'); + } + }); + + it('should not forward record request and give 500', async () => { + try{ + await request(app) + .post('/record') + .set('token', 'valorDelToken');; + + } catch(error){ + expect(error.response.status).toBe(500); + expect(error.response.data.error).toBe('Internal server error'); + } + }); + + it('should not forward record request and give 500', async () => { + try{ + await request(app) + .get('/record/ranking/top10') + .set('token', 'valorDelToken');; + + } catch(error){ + expect(error.response.status).toBe(500); + expect(error.response.data.error).toBe('Internal server error'); + } + }); + + it('should not forward record request and give 500', async () => { + try{ + await request(app) + .get('/record/ranking/user') + .set('token', 'valorDelToken');; + + } catch(error){ + expect(error.response.status).toBe(500); + expect(error.response.data.error).toBe('Internal server error'); + } + }); + + it('should not forward record request and give 500', async () => { + try{ + await request(app) + .get('/record/user') + .set('token', 'valorDelToken');; + + } catch(error){ + expect(error.response.status).toBe(500); + expect(error.response.data.error).toBe('Internal server error'); + } + }); }); diff --git a/questionGenerator/src/main/java/Main.java b/questionGenerator/src/main/java/Main.java index cd0480f..b5ac6cb 100644 --- a/questionGenerator/src/main/java/Main.java +++ b/questionGenerator/src/main/java/Main.java @@ -13,9 +13,9 @@ public class Main { private static QuestionGenerator qg = QuestionGenerator.getInstance(); - private static final long TIME_SKIP = 18000000; //5 hours + // private static final long TIME_SKIP = 18000000; //5 hours // private static final long TIME_SKIP = 43200000; //12 hours - //private static final long TIME_SKIP = 1000; //1 minute + private static final long TIME_SKIP = 10000; //10 minutes private static String[] languages = {"en", "es", "tr"}; @@ -32,10 +32,12 @@ public class Main { public static void main(String[] args) { List questions = generate().stream().map(q -> q.getJSON().toString()).toList(); while(true) { + long timeStart = System.currentTimeMillis(); QuestionRepository.getInstance().populate(questions); questions = generate().stream().map(q -> q.getJSON().toString()).toList(); + long timeFinish = System.currentTimeMillis(); try { - Thread.sleep(TIME_SKIP); + Thread.sleep(TIME_SKIP - (timeFinish - timeStart)); } catch (InterruptedException e) { e.printStackTrace(); } diff --git a/webapp/package-lock.json b/webapp/package-lock.json index e71a4dd..1284ec2 100644 --- a/webapp/package-lock.json +++ b/webapp/package-lock.json @@ -12,7 +12,6 @@ "@emotion/styled": "^11.11.0", "@mui/material": "^5.15.3", "@testing-library/jest-dom": "^5.17.0", - "@testing-library/react": "^14.1.2", "@testing-library/user-event": "^14.5.2", "axios": "^1.6.5", "bcrypt": "^5.1.1", @@ -34,6 +33,7 @@ }, "devDependencies": { "@babel/plugin-proposal-private-property-in-object": "^7.21.11", + "@testing-library/react": "^15.0.5", "axios-mock-adapter": "^1.22.0", "expect-puppeteer": "^9.0.2", "jest": "^29.3.1", @@ -5451,21 +5451,21 @@ } }, "node_modules/@testing-library/dom": { - "version": "9.3.3", - "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.3.3.tgz", - "integrity": "sha512-fB0R+fa3AUqbLHWyxXa2kGVtf1Fe1ZZFr0Zp6AIbIAzXb2mKbEXl+PCQNUOaq5lbTab5tfctfXRNsWXxa2f7Aw==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.0.0.tgz", + "integrity": "sha512-PmJPnogldqoVFf+EwbHvbBJ98MmqASV8kLrBYgsDNxQcFMeIS7JFL48sfyXvuMtgmWO/wMhh25odr+8VhDmn4g==", "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", "@types/aria-query": "^5.0.1", - "aria-query": "5.1.3", + "aria-query": "5.3.0", "chalk": "^4.1.0", "dom-accessibility-api": "^0.5.9", "lz-string": "^1.5.0", "pretty-format": "^27.0.2" }, "engines": { - "node": ">=14" + "node": ">=18" } }, "node_modules/@testing-library/dom/node_modules/ansi-styles": { @@ -5482,14 +5482,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@testing-library/dom/node_modules/aria-query": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz", - "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==", - "dependencies": { - "deep-equal": "^2.0.5" - } - }, "node_modules/@testing-library/dom/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -5623,16 +5615,17 @@ } }, "node_modules/@testing-library/react": { - "version": "14.1.2", - "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-14.1.2.tgz", - "integrity": "sha512-z4p7DVBTPjKM5qDZ0t5ZjzkpSNb+fZy1u6bzO7kk8oeGagpPCAtgh4cx1syrfp7a+QWkM021jGqjJaxJJnXAZg==", + "version": "15.0.5", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-15.0.5.tgz", + "integrity": "sha512-ttodVWYA2i2w4hRa6krKrmS1vKxAEkwDz34y+CwbcrbZUxFzUYN3a5xZyFKo+K6LBseCRCUkwcjATpaNn/UsIA==", + "dev": true, "dependencies": { "@babel/runtime": "^7.12.5", - "@testing-library/dom": "^9.0.0", + "@testing-library/dom": "^10.0.0", "@types/react-dom": "^18.0.0" }, "engines": { - "node": ">=14" + "node": ">=18" }, "peerDependencies": { "react": "^18.0.0", @@ -5982,6 +5975,7 @@ "version": "18.2.18", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.18.tgz", "integrity": "sha512-TJxDm6OfAX2KJWJdMEVTwWke5Sc/E/RlnPGvGfS0W7+6ocy2xhDVQVh/KvC2Uf7kACs+gDytdusDSdWfWkaNzw==", + "dev": true, "dependencies": { "@types/react": "*" } @@ -9300,37 +9294,6 @@ } } }, - "node_modules/deep-equal": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.3.tgz", - "integrity": "sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==", - "dependencies": { - "array-buffer-byte-length": "^1.0.0", - "call-bind": "^1.0.5", - "es-get-iterator": "^1.1.3", - "get-intrinsic": "^1.2.2", - "is-arguments": "^1.1.1", - "is-array-buffer": "^3.0.2", - "is-date-object": "^1.0.5", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "isarray": "^2.0.5", - "object-is": "^1.1.5", - "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.5.1", - "side-channel": "^1.0.4", - "which-boxed-primitive": "^1.0.2", - "which-collection": "^1.0.1", - "which-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/deep-extend": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", @@ -9865,25 +9828,6 @@ "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==" }, - "node_modules/es-get-iterator": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz", - "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==", - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", - "has-symbols": "^1.0.3", - "is-arguments": "^1.1.1", - "is-map": "^2.0.2", - "is-set": "^2.0.2", - "is-string": "^1.0.7", - "isarray": "^2.0.5", - "stop-iteration-iterator": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/es-iterator-helpers": { "version": "1.0.15", "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.15.tgz", @@ -12729,21 +12673,6 @@ "node": ">= 0.10" } }, - "node_modules/is-arguments": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", - "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-array-buffer": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", @@ -20066,21 +19995,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/object-is": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", - "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", @@ -25918,17 +25832,6 @@ "node": ">= 0.8" } }, - "node_modules/stop-iteration-iterator": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz", - "integrity": "sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==", - "dependencies": { - "internal-slot": "^1.0.4" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/stream-combiner": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", diff --git a/webapp/package.json b/webapp/package.json index c258e1f..3e23cdd 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -7,7 +7,6 @@ "@emotion/styled": "^11.11.0", "@mui/material": "^5.15.3", "@testing-library/jest-dom": "^5.17.0", - "@testing-library/react": "^14.1.2", "@testing-library/user-event": "^14.5.2", "axios": "^1.6.5", "bcrypt": "^5.1.1", @@ -55,6 +54,7 @@ }, "devDependencies": { "@babel/plugin-proposal-private-property-in-object": "^7.21.11", + "@testing-library/react": "^15.0.5", "axios-mock-adapter": "^1.22.0", "expect-puppeteer": "^9.0.2", "jest": "^29.3.1", diff --git a/webapp/src/App.js b/webapp/src/App.js index 07b0c74..d7895c6 100644 --- a/webapp/src/App.js +++ b/webapp/src/App.js @@ -32,7 +32,8 @@ function App() {
- + + } /> @@ -49,7 +50,9 @@ function App() { } /> } /> + +
diff --git a/webapp/src/components/ForgetPassword/AskEmailUsername.js b/webapp/src/components/ForgetPassword/AskEmailUsername.js new file mode 100644 index 0000000..c936cde --- /dev/null +++ b/webapp/src/components/ForgetPassword/AskEmailUsername.js @@ -0,0 +1,12 @@ +import "../../custom.css"; +import InputEmailAndUsername from "../loginAndRegistration/InputEmailAndUsername" + +export default function AskEmailUsername({ email, setEmail, username, setUsername, t, handleSubmit, showErrors }) { + return ( +
+ < InputEmailAndUsername title={"forgotPassword.enter_email"} email={email} setEmail={setEmail} username={username} setUsername={setUsername} showErrors={showErrors} t={t}/> + +
+
+ ); + } \ No newline at end of file diff --git a/webapp/src/components/ForgetPassword/AskEmailUsername.test.js b/webapp/src/components/ForgetPassword/AskEmailUsername.test.js new file mode 100644 index 0000000..a7cec1e --- /dev/null +++ b/webapp/src/components/ForgetPassword/AskEmailUsername.test.js @@ -0,0 +1,70 @@ +import { render, fireEvent } from '@testing-library/react'; +import '@testing-library/jest-dom'; // Ensures custom assertions work +import AskEmailUsername from './AskEmailUsername'; // Path to the component to be tested + +// Mock for useTranslation +jest.mock('react-i18next', () => ({ + useTranslation: jest.fn().mockReturnValue({ + t: (key) => key, // Simulate translation by returning the key + }), +})); + +// Test for the component +describe('AskEmailUsername Component', () => { + it('should render the form with email and username inputs', () => { + const mockHandleSubmit = jest.fn(); + const mockShowErrors = jest.fn().mockReturnValue(null); // Simulate no errors + + const { getByText, getByPlaceholderText } = render( + key} // Mock for translation + handleSubmit={mockHandleSubmit} + showErrors={mockShowErrors} + /> + ); + + // Verify that the title and inputs are rendered + expect(getByText('forgotPassword.enter_email')).toBeInTheDocument(); + expect(getByPlaceholderText('addUser.email_placeholder')).toBeInTheDocument(); + expect(getByPlaceholderText('addUser.username_placeholder')).toBeInTheDocument(); + + // Simulate the submit event + const form = getByText('forgotPassword.enter_email_button').closest('form'); + fireEvent.submit(form); + + // Verify that handleSubmit is called + expect(mockHandleSubmit).toHaveBeenCalled(); + }); + + it('should call setEmail and setUsername on input change', () => { + const mockSetEmail = jest.fn(); + const mockSetUsername = jest.fn(); + + const { getByPlaceholderText } = render( + key} + handleSubmit={jest.fn()} // No need to verify handleSubmit here + showErrors={jest.fn()} + /> + ); + + // Simulate input change for email and username + const emailInput = getByPlaceholderText('addUser.email_placeholder'); + const usernameInput = getByPlaceholderText('addUser.username_placeholder'); + + fireEvent.change(emailInput, { target: { value: 'test@example.com' } }); + fireEvent.change(usernameInput, { target: { value: 'testuser' } }); + + // Verify that setEmail and setUsername were called with correct values + expect(mockSetEmail).toHaveBeenCalledWith('test@example.com'); + expect(mockSetUsername).toHaveBeenCalledWith('testuser'); + }); +}); diff --git a/webapp/src/components/ForgetPassword/EnterCode.js b/webapp/src/components/ForgetPassword/EnterCode.js new file mode 100644 index 0000000..40ee9c5 --- /dev/null +++ b/webapp/src/components/ForgetPassword/EnterCode.js @@ -0,0 +1,20 @@ +export default function EnterCode({ t, obtainCode, showErrors }) { + return ( +
+
+

{t("forgotPassword.enter_code")}

+ {showErrors()} +
+ + + + + + +
+ +
+
+
+ ); + } \ No newline at end of file diff --git a/webapp/src/components/ForgetPassword/EnterCode.test.js b/webapp/src/components/ForgetPassword/EnterCode.test.js new file mode 100644 index 0000000..1e425c5 --- /dev/null +++ b/webapp/src/components/ForgetPassword/EnterCode.test.js @@ -0,0 +1,53 @@ +import React from 'react'; +import { render, fireEvent } from '@testing-library/react'; +import '@testing-library/jest-dom/extend-expect'; // Permite usar aserciones extendidas +import EnterCode from './EnterCode'; // Ruta al componente + +// Mock de `useTranslation` +jest.mock('react-i18next', () => ({ + useTranslation: jest.fn().mockReturnValue({ + t: (key) => key, // Simula la traducción devolviendo la clave + }), +})); + +describe('EnterCode Component', () => { + it('should render correctly with six input fields', () => { + const obtainCodeMock = jest.fn(); + const showErrorsMock = jest.fn(); // Mock de función para mostrar errores + + const { getAllByPlaceholderText, getByText } = render( + key} + obtainCode={obtainCodeMock} + showErrors={showErrorsMock} + /> + ); + + // Verificar que se rendericen seis campos de entrada + const inputFields = getAllByPlaceholderText('X'); + expect(inputFields.length).toBe(6); // Espera seis campos de entrada + + // Verificar que el título y el botón de envío están presentes + expect(getByText('forgotPassword.enter_code')).toBeInTheDocument(); + expect(getByText('forgotPassword.send_code')).toBeInTheDocument(); + }); + + it('should call obtainCode when the form is submitted', () => { + const obtainCodeMock = jest.fn(); + const showErrorsMock = jest.fn(); + const { getByText } = render( + key} + obtainCode={obtainCodeMock} + showErrors={showErrorsMock} + /> + ); + + // Simular el evento de envío del formulario + const form = getByText('forgotPassword.send_code').closest('form'); + fireEvent.submit(form); + + // Verificar que `obtainCode` fue llamado al enviar el formulario + expect(obtainCodeMock).toHaveBeenCalled(); + }); +}); diff --git a/webapp/src/components/ForgetPassword/ForgetPassword.js b/webapp/src/components/ForgetPassword/ForgetPassword.js index c00bc34..12b073b 100644 --- a/webapp/src/components/ForgetPassword/ForgetPassword.js +++ b/webapp/src/components/ForgetPassword/ForgetPassword.js @@ -7,6 +7,10 @@ import zxcvbn from "zxcvbn"; import { useNavigate } from "react-router-dom"; import { validateEmail, validatePasswords, validateUsername } from "../../utils/utils"; +import AskEmailUsername from "./AskEmailUsername"; +import PendingComponent from "./PendingComponent"; +import EnterCode from "./EnterCode"; +import RestorePassword from "./RestorePassword"; const forgetPasswordFunctions = new ForgetPasswordFunctions(); export default function ForgotPassword() { @@ -219,137 +223,3 @@ export default function ForgotPassword() { ); } - -// Sub-components used in the main component -function AskEmailUsername({ email, setEmail, username, setUsername, t, handleSubmit, showErrors }) { - return ( -
-

{t("forgotPassword.enter_email")}

- {showErrors()} -
-

{t("addUser.email_placeholder")}:

- setEmail(e.target.value)} - /> -
-
-

{t("addUser.username_placeholder")}:

- setUsername(e.target.value)} - /> -
- -
-
- ); -} - -function PendingComponent({ t }) { - return ( -
-

{t("forgotPassword.sending_title")}

-

{t("forgotPassword.sending_paragraph")}

-
- ); -} - -function EnterCode({ t, obtainCode, showErrors }) { - return ( -
-
-

{t("forgotPassword.enter_code")}

- {showErrors()} -
- - - - - - -
- -
-
-
- ); -} - -function RestorePassword({ - email, - username, - passwordStrength, - passwordStrengthText, - newPassword, - handlePasswordChange, - repeatPassword, - setRepeatPassword, - handleSubmit, - t, - showErrors -}) { - return ( -
-

{t("forgotPassword.enter_password")}

- {showErrors()} -
-

{t("addUser.email_placeholder")}:

- -
-
-

{t("addUser.username_placeholder")}:

- -
-
-

{t("addUser.password_placeholder")}:

- -
-
- {t(passwordStrengthText)} - -
-
-

{t("addUser.repeat_password_placeholder")}:

- setRepeatPassword(e.target.value)} - /> -
- -
-
- ); -} diff --git a/webapp/src/components/ForgetPassword/ForgetPassword.test.js b/webapp/src/components/ForgetPassword/ForgetPassword.test.js index 1c6b166..eb622de 100644 --- a/webapp/src/components/ForgetPassword/ForgetPassword.test.js +++ b/webapp/src/components/ForgetPassword/ForgetPassword.test.js @@ -19,6 +19,10 @@ i18en.use(initReactI18next).init({ }); global.i18en = i18en; const mockAxios = new MockAdapter(axios); + +const code = 111111 +const token = "mockedToken" + jest.mock('axios'); describe('ForgetPassword Component', () => { test('renders Ask Email component', async () => { @@ -29,42 +33,41 @@ describe('ForgetPassword Component', () => { expect(screen.getByText(/addUser.email_placeholder/)).toBeInTheDocument(); expect(screen.getByText(/addUser.username_placeholder/)).toBeInTheDocument(); }); - test('renders AskForCode component', async () => { - var code = 111111 - var token = "mockedToken" - act( () => { - render(); + + test('show email errors', async () => { + await act( () => { + render(); }); await waitFor(() => expect(screen.getByText(i18en.t("forgotPassword.enter_email"))).toBeInTheDocument()); - const emailInput = screen.getByPlaceholderText(/addUser.email_placeholder/i); - const usernameInput = screen.getByPlaceholderText(/addUser.username_placeholder/i); - //introducimos email y username - expect(screen.getByText(/addUser.email_placeholder/)).toBeInTheDocument(); - userEvent.type(emailInput, 'test@example.com'); - userEvent.type(usernameInput, 'testuser'); + + await act(async () => { await insertEmail('testexample.com', 'testuser') }) - // Hacer clic en el botón de enviar - const submitButton = screen.getByText(/forgotPassword.enter_email_button/i); // Ajusta el texto según el texto real del botón - userEvent.click(submitButton); + await waitFor(() => { + expect(screen.getByText('addUser.error_wrong_email_format')).toBeInTheDocument(); + }) + }) + + + test('change password', async () => { + act( () => { + render(); + }); mockAxios.onPost('http://localhost:8000/forgetPassword').reply(200, "Email sent"); + await waitFor(() => expect(screen.getByText(i18en.t("forgotPassword.enter_email"))).toBeInTheDocument()); + insertEmail('test@example.com', 'testuser') + + act(async ()=>{ await waitFor(async () => expect(screen.getByText(i18en.t("forgotPassword.enter_code")).toBeInTheDocument())); - const inputs = screen.getAllByPlaceholderText('X'); - // Introducir el mismo carácter en todos los inputs - userEvent.type(inputs[0], '1'); // Introducir el carácter '1', puedes cambiarlo al que desees - userEvent.type(inputs[1], '1'); - userEvent.type(inputs[2], '1'); - userEvent.type(inputs[3], '1'); - userEvent.type(inputs[4], '1'); - userEvent.type(inputs[5], '1'); - // Simula un clic en el botón de submit - fireEvent.click(screen.getByText(/"forgotPassword.send_code"/)); mockAxios.onGet('http://localhost:8000/tokenFromCode/' + code).reply(200, {token: token}); + insertCode(['1', '1', '1', '1' ,'1' ,'1' ,'1']) + //llegamos al replace await waitFor(async () => expect(screen.getByText(i18en.t("forgotPassword.enter_password")).toBeInTheDocument())); mockAxios.onPost('http://localhost:8000/changePassword').reply(200, { token: token, username: username}); + await insertPassword('123456789', '123456789') //me redirigen a game Menu await waitFor(async () => expect(screen.getByText(i18en.t('gameMenu.title')).toBeInTheDocument())); //la cookie queda bien seteada @@ -72,6 +75,7 @@ describe('ForgetPassword Component', () => { }); }); + describe('ForgetPasswordFunctions', () => { let forgetPasswordFunctions; @@ -120,5 +124,41 @@ describe('ForgetPassword Component', () => { }); }); }); - }); + + + +async function insertEmail(email, username) { + const emailInput = screen.getByPlaceholderText(/addUser.email_placeholder/i); + const usernameInput = screen.getByPlaceholderText(/addUser.username_placeholder/i); + //introducimos email y username + expect(screen.getByText(/addUser.email_placeholder/)).toBeInTheDocument(); + userEvent.type(emailInput, email); + userEvent.type(usernameInput, username); + const submitButton = screen.getByText(/forgotPassword.enter_email_button/i); // Ajusta el texto según el texto real del botón + userEvent.click(submitButton); +} + +async function insertCode(code){ + const inputs = screen.getAllByPlaceholderText('X'); + // Introducir el mismo carácter en todos los inputs + userEvent.type(inputs[0], code[0]); + userEvent.type(inputs[1], code[1]); + userEvent.type(inputs[2], code[2]); + userEvent.type(inputs[3], code[3]); + userEvent.type(inputs[4], code[4]); + userEvent.type(inputs[5], code[5]); + // Simula un clic en el botón de submit + fireEvent.click(screen.getByText(/"forgotPassword.send_code"/)); +} + +async function insertPassword(password, repeatPassword) { + const passwordInput = screen.getByPlaceholderText(/addUser.password_placeholder/i); + const passwordRepeatInput = screen.getByPlaceholderText(/addUser.repeat_password_placeholder/i); + + expect(screen.getByText(/addUser.email_placeholder/)).toBeInTheDocument(); + userEvent.type(passwordInput, password); + userEvent.type(passwordRepeatInput, repeatPassword); + const submitButton = screen.getByText(/forgotPassword.enter_password_button/i); // Ajusta el texto según el texto real del botón + userEvent.click(submitButton); +} \ No newline at end of file diff --git a/webapp/src/components/ForgetPassword/PendingComponent.js b/webapp/src/components/ForgetPassword/PendingComponent.js new file mode 100644 index 0000000..46e0cad --- /dev/null +++ b/webapp/src/components/ForgetPassword/PendingComponent.js @@ -0,0 +1,10 @@ + +export default function PendingComponent({ t }) { + return ( +
+

{t("forgotPassword.sending_title")}

+

{t("forgotPassword.sending_paragraph")}

+
+ ); +} + \ No newline at end of file diff --git a/webapp/src/components/ForgetPassword/PendingComponent.test.js b/webapp/src/components/ForgetPassword/PendingComponent.test.js new file mode 100644 index 0000000..1dd7497 --- /dev/null +++ b/webapp/src/components/ForgetPassword/PendingComponent.test.js @@ -0,0 +1,28 @@ +import React from 'react'; +import { render, screen, fireEvent, waitFor } from '@testing-library/react'; +import { MemoryRouter } from 'react-router-dom'; +import { act } from 'react-dom/test-utils'; +import { initReactI18next } from 'react-i18next'; +import i18en from 'i18next'; +import PendingComponent from './PendingComponent'; + + +i18en.use(initReactI18next).init({ + resources: {}, + lng: 'en', + interpolation:{ + escapeValue: false, + } +}); +global.i18en = i18en; + + +describe('Pending Component', () => { + test('renders Ask Email component', async () => { + act( () => { + render(); + }); + await waitFor(() => expect(screen.getByText(/forgotPassword.sending_title/)).toBeInTheDocument()); + expect(screen.getByText(/forgotPassword.sending_paragraph/)).toBeInTheDocument(); + }); +}); diff --git a/webapp/src/components/ForgetPassword/RestorePassword.js b/webapp/src/components/ForgetPassword/RestorePassword.js new file mode 100644 index 0000000..1ac505e --- /dev/null +++ b/webapp/src/components/ForgetPassword/RestorePassword.js @@ -0,0 +1,70 @@ +export default function RestorePassword({ + email, + username, + passwordStrength, + passwordStrengthText, + newPassword, + handlePasswordChange, + repeatPassword, + setRepeatPassword, + handleSubmit, + t, + showErrors + }) { + return ( +
+

{t("forgotPassword.enter_password")}

+ {showErrors()} +
+

{t("addUser.email_placeholder")}:

+ +
+
+

{t("addUser.username_placeholder")}:

+ +
+
+

{t("addUser.password_placeholder")}:

+ +
+
+ {t(passwordStrengthText)} + +
+
+

{t("addUser.repeat_password_placeholder")}:

+ setRepeatPassword(e.target.value)} + /> +
+ +
+
+ ); + } + \ No newline at end of file diff --git a/webapp/src/components/ForgetPassword/RestorePassword.test.js b/webapp/src/components/ForgetPassword/RestorePassword.test.js new file mode 100644 index 0000000..626099f --- /dev/null +++ b/webapp/src/components/ForgetPassword/RestorePassword.test.js @@ -0,0 +1,101 @@ +import { screen, render, fireEvent } from '@testing-library/react'; +import '@testing-library/jest-dom'; // Ensures custom assertions are available +import RestorePassword from './RestorePassword'; // Path to the component to test + +// Mock for useTranslation +jest.mock('react-i18next', () => ({ + useTranslation: jest.fn().mockReturnValue({ + t: (key) => key, // Simulate translation by returning the key + }), +})); + +describe('RestorePassword Component', () => { + it('should render the form with all expected fields', () => { + const mockHandleSubmit = jest.fn(); // Mock for form submission + const mockHandlePasswordChange = jest.fn(); + const mockShowErrors = jest.fn().mockReturnValue(null); // Simulate no errors + + const { getByText } = render( + key} // Mock for translation + showErrors={mockShowErrors} + /> + ); + + // Verify key elements are rendered + expect(getByText('forgotPassword.enter_password')).toBeInTheDocument(); + expect(getByText(/addUser.password_placeholder/i)).toBeInTheDocument(); + expect(getByText(/addUser.repeat_password_placeholder/i)).toBeInTheDocument(); + + // Check if the email and username are read-only and have correct values + const inputs = screen.getAllByRole('textbox'); + expect(inputs[0].value).toBe('test@example.com'); + expect(inputs[1].value).toBe('testuser'); + }); + + it('should call handlePasswordChange on password input change', () => { + const mockHandlePasswordChange = jest.fn(); + + const { container } = render( + key} + showErrors={jest.fn()} + /> + ); + + // Simulate changing the password input + + const passwordInput = container.querySelector(`input[name="password"]`); + fireEvent.change(passwordInput, { target: { value: 'newPassword' } }); + + const rPasswordInput = container.querySelector(`input[name="repeat_password"]`); + fireEvent.change(rPasswordInput, { target: { value: 'newPassword' } }); + // Ensure handlePasswordChange was called with the correct argument + expect(mockHandlePasswordChange).toHaveBeenCalledWith(expect.any(Object)); // Expecting event object + }); + + it('should call handleSubmit on form submission', () => { + const mockHandleSubmit = jest.fn(); + + const { getByText } = render( + key} + showErrors={jest.fn()} + /> + ); + + // Simulate form submission + const form = getByText('forgotPassword.enter_password_button').closest('form'); + fireEvent.submit(form); + + // Verify handleSubmit was called on form submission + expect(mockHandleSubmit).toHaveBeenCalled(); + }); +}); diff --git a/webapp/src/components/loginAndRegistration/AddUser.js b/webapp/src/components/loginAndRegistration/AddUser.js index cabc827..08a1c42 100644 --- a/webapp/src/components/loginAndRegistration/AddUser.js +++ b/webapp/src/components/loginAndRegistration/AddUser.js @@ -7,6 +7,7 @@ import { useState } from 'react'; import { useNavigate } from 'react-router-dom'; import zxcvbn from "zxcvbn"; import Cookies from 'js-cookie'; +import InputEmailAndUsername from './InputEmailAndUsername' import { manageError, validateEmail, validateUsername, validatePasswords } from "../../utils/utils"; @@ -95,30 +96,7 @@ const AddUser = () => {
-

{t("addUser.title")}

- {showErrors()} -
-

{t("addUser.email_placeholder")}:

- setEmail(e.target.value)} - /> -
-
-

{t("addUser.username_placeholder")}:

- setUsername(e.target.value)} - /> -
+ < InputEmailAndUsername title={"addUser.title"} email={email} setEmail={setEmail} username={username} setUsername={setUsername} showErrors={showErrors} t={t}/>

{t("addUser.password_placeholder")}:

+

{t(title)}

+ {showErrors()} +
+

{t("addUser.email_placeholder")}:

+ setEmail(e.target.value)} + /> +
+
+

{t("addUser.username_placeholder")}:

+ setUsername(e.target.value)} + /> +
+ + ); + } \ No newline at end of file diff --git a/webapp/src/custom.css b/webapp/src/custom.css index 5cc05a4..73c69fa 100644 --- a/webapp/src/custom.css +++ b/webapp/src/custom.css @@ -115,6 +115,15 @@ margin: 0.5em; } + .user-button{ + margin-right: 10px; + box-shadow: 0 0 10px black; + cursor:pointer; + font-size: 20px; + font-weight: 700; + margin: 0.5em; + } + .right-nav{ display: flex; }