Skip to content

Commit

Permalink
Merge pull request #65 from Arquisoft/feat/webapp/new-signup
Browse files Browse the repository at this point in the history
Feat/webapp/new signup
  • Loading branch information
jjgancfer authored Mar 4, 2024
2 parents 9f3b296 + 17beee4 commit b2d0b56
Show file tree
Hide file tree
Showing 5 changed files with 191 additions and 98 deletions.
3 changes: 2 additions & 1 deletion webapp/public/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
"session": {
"username": "Username",
"password": "Password",
"email": "Email"
"email": "Email",
"confirm_password": "Confirm password"
},
"error": {
"login": "An ERROR occurred during login"
Expand Down
3 changes: 2 additions & 1 deletion webapp/public/locales/es/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
"session": {
"username": "Nombre de usuario",
"password": "Contraseña",
"email": "Correo electrónico"
"email": "Correo electrónico",
"confirm_password": "Confirmar contraseña"
},
"error": {
"login": "Ocurrió un ERROR en el login"
Expand Down
6 changes: 4 additions & 2 deletions webapp/src/components/Router.jsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import React from "react";
import Root from "../pages/Root";
import Login from "../pages/Login";
import { Route, createRoutesFromElements } from "react-router-dom";
import Signup from "../pages/Signup";
import { Route,createRoutesFromElements } from "react-router-dom";

export default createRoutesFromElements(
<Route path="/" >
<Route path="/">
<Route index element={<Root />} />
<Route path="/signup" element={<Signup />} />
<Route path="/login" element={<Login />}/>
</Route>
)
152 changes: 119 additions & 33 deletions webapp/src/pages/Signup.jsx
Original file line number Diff line number Diff line change
@@ -1,53 +1,139 @@
import { Center } from "@chakra-ui/layout";
import { Button, FormControl, FormLabel, Heading, Input, Text, Stack } from "@chakra-ui/react";
import { Heading, Input, Button, InputGroup, Stack, InputLeftElement, chakra, Box, Avatar, FormControl, InputRightElement, Text, FormHelperText } from "@chakra-ui/react";
import { ViewIcon, ViewOffIcon } from '@chakra-ui/icons'
import axios, { HttpStatusCode } from "axios";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { useNavigate } from "react-router-dom";
import { FaUserAlt, FaLock, FaAddressCard } from "react-icons/fa";
import ButtonEf from '../components/ButtonEf';

export default function Signup() {

const [email, setEmail] = useState("");
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const [confirmPassword, setConfirmPassword] = useState("");
const [showPassword, setShowPassword] = useState(false);
const [showConfirmPassword, setShowConfirmPassword] = useState(false);
const [hasError, setHasError] = useState(false);

const navigate = useNavigate();
const { t } = useTranslation();

const ChakraFaCardAlt = chakra(FaAddressCard);
const ChakraFaUserAlt = chakra(FaUserAlt);
const ChakraFaLock = chakra(FaLock);

const sendLogin = async () => {
let data = {};
let response = await axios.post(process.env.API_URL, data);
if (response.status === HttpStatusCode.Accepted) {
navigate("/home");
} else {
try {
const response = await axios.post(process.env.API_URL, { email, username, password });
if (response.status === HttpStatusCode.Accepted) {
navigate("/home");
}
} catch (error) {
setHasError(true);
}
}
};

return (
<Center display={"flex"} flexDirection={"column"} maxW={"100%"} minW={"30%"} mt={"2vh"}>
<Heading as="h2">{ t("common.register")}</Heading>
{
!hasError ?
<></> :
<Center bgColor={"#FFA98A"} margin={"1vh 0vw"} padding={"1vh 0vw"}
color={"#FF0500"} border={"0.1875em solid #FF0500"}
borderRadius={"0.75em"} maxW={"100%"} minW={"30%"}>
<Center
display={"flex"}
flexDirection={"column"}
w={"100wh"}
h={"100vh"}
bg={"blue.50"}
justifyContent={"center"}
alignItems={"center"}
>
{hasError && (
<div className="error-container">
<Text>Error</Text>
</div>
)}
<Stack flexDir={"column"} mb="2" justifyContent="center" alignItems={"center"}>
<Avatar bg="blue.500" />
<Heading as="h2" color="blue.400">
{t("common.register")}
</Heading>
{!hasError ? (
<></>
) : (
<Center
bgColor={"#FFA98A"}
margin={"1vh 0vw"}
padding={"1vh 0vw"}
color={"#FF0500"}
border={"0.1875em solid #FF0500"}
borderRadius={"0.75em"}
maxW={"100%"}
minW={"30%"}
>
<Text>Error</Text>
</Center>
}
<Stack spacing={4} mt={4} width="100%" mx={"auto"} maxWidth={"400px"}>
<FormControl as="fieldset" padding={"1vh 0vw"} isRequired>
<FormLabel>{ t("session.username") }</FormLabel>
<Input type="text" />
</FormControl>
<FormControl as="fieldset" padding={"1vh 0vw"} isRequired>
<FormLabel>{ t("Correo electrónico") }</FormLabel> {/* To be changed */}
<Input type="text" />
</FormControl>
<FormControl as="fieldset" padding={"1vh 0vw"} isRequired>
<FormLabel> {t("session.password")}</FormLabel>
<Input type="password" />
</FormControl>
<Button type="submit" onClick={sendLogin}>Enviar</Button>
</Center>
)}
<Box minW={{ md: "400px" }}>
<Stack spacing={4} p="1rem" backgroundColor="whiteAlpha.900" boxShadow="md">
<FormControl>
<InputGroup>
<InputLeftElement children={<ChakraFaCardAlt color="gray.300" />} />
<Input
type="text"
placeholder={t("session.email")}
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
</InputGroup>
</FormControl>
<FormControl>
<InputGroup>
<InputLeftElement children={<ChakraFaUserAlt color="gray.300" />} />
<Input
type="text"
placeholder={t("session.username")}
value={username}
onChange={(e) => setUsername(e.target.value)}
/>
</InputGroup>
</FormControl>
<FormControl>
<InputGroup>
<InputLeftElement children={<ChakraFaLock color="gray.300" />} />
<Input
type={showPassword ? "text" : "password"}
placeholder={t("session.password")}
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
<InputRightElement width="4.5rem">
<Button data-testid="show-confirm-password-button" h="1.75rem" size="sm" onClick={() => setShowPassword(!showPassword)}>
{showPassword ? <ViewOffIcon/> : <ViewIcon/>}
</Button>
</InputRightElement>
</InputGroup>
</FormControl>
<FormControl>
<InputGroup>
<InputLeftElement children={<ChakraFaLock color="gray.300" />} />
<Input
type={showConfirmPassword ? "text" : "password"}
placeholder={t("session.confirm_password")}
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
/>
<InputRightElement width="4.5rem">
<Button data-testid="show-confirm-password-button" h="1.75rem" size="sm" onClick={() => setShowConfirmPassword(!showConfirmPassword)}>
{showConfirmPassword ? <ViewOffIcon/> : <ViewIcon/>}
</Button>
</InputRightElement>
</InputGroup>
{confirmPassword && password && confirmPassword !== password && (
<FormHelperText color="red">Las contraseñas no coinciden</FormHelperText>
)}
</FormControl>
<ButtonEf dataTestId={"Sign up"} variant={"solid"} colorScheme={"blue"} text={t("common.register")} onClick={sendLogin}/>
</Stack>
</Box>
</Stack>
</Center>
);
}
}
125 changes: 64 additions & 61 deletions webapp/src/tests/Login.test.js → webapp/src/tests/Signup.test.js
Original file line number Diff line number Diff line change
@@ -1,61 +1,64 @@
import React from 'react';
import { render, fireEvent, screen, waitFor, act, getByLabelText, getByTestId } from '@testing-library/react';
import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
import Login from '../pages/Login';
import { MemoryRouter, createMemoryRouter } from 'react-router';
import router from '../components/Router';

const mockAxios = new MockAdapter(axios);
const mockRouter = createMemoryRouter(router);

describe('Login component', () => {
beforeEach(() => {
mockAxios.reset();
});

it('renders form elements correctly', async () => {
const { getByPlaceholderText, getByText } = render(<MemoryRouter><Login /></MemoryRouter>);

expect(getByPlaceholderText('session.email')).toBeInTheDocument();
expect(getByPlaceholderText('session.password')).toBeInTheDocument();
expect(getByTestId(document.body, 'Login')).toBeInTheDocument();
});

it('toggles password visibility', () => {
const { getByPlaceholderText, getByText } = render(<MemoryRouter><Login /></MemoryRouter>);

const passwordInput = getByPlaceholderText('session.password');
const showPasswordButton = getByTestId(document.body, 'togglePasswordButton');

fireEvent.click(showPasswordButton);

expect(passwordInput.getAttribute('type')).toBe('text');
});

it('submits form data correctly', async () => {
const axiosMock = jest.spyOn(axios, 'post');
axiosMock.mockResolvedValueOnce({ status: 202 }); // Accepted status code

// Render the Signup component
const { getByPlaceholderText, getByText } = render(<MemoryRouter><Login /></MemoryRouter>);

// Get form elements and submit button by their text and placeholder values
const emailInput = getByPlaceholderText('session.email');
const passwordInput = getByPlaceholderText('session.password');
const signUpButton = getByTestId(document.body, 'Login');

// Fill out the form with valid data and submit it
fireEvent.change(emailInput, { target: { value: '[email protected]' } });
fireEvent.change(passwordInput, { target: { value: 'password' } });
fireEvent.click(signUpButton);

// Check if the form data was sent correctly
await waitFor(() => {
expect(axiosMock).toHaveBeenCalledWith(process.env.API_URL, {});
expect(axiosMock).toHaveBeenCalledTimes(1);
});

axiosMock.mockRestore();
});
});
import React from 'react';
import { render, fireEvent, waitFor, getByTestId, getAllByTestId } from '@testing-library/react';
import axios from 'axios';
import { MemoryRouter, createMemoryRouter } from 'react-router';
import Signup from '../pages/Signup';

describe('Signup Component', () => {

it('renders form elements correctly', () => {
const { getByPlaceholderText, getByText } = render(<MemoryRouter><Signup /></MemoryRouter>);

expect(getByPlaceholderText('session.email')).toBeInTheDocument();
expect(getByPlaceholderText('session.username')).toBeInTheDocument();
expect(getByPlaceholderText('session.password')).toBeInTheDocument();
expect(getByPlaceholderText('session.confirm_password')).toBeInTheDocument();
expect(getByTestId(document.body, 'Sign up')).toBeInTheDocument();
});

it('toggles password visibility', () => {
const { getByPlaceholderText, getAllByRole } = render(<MemoryRouter><Signup /></MemoryRouter>);

const passwordInput = getByPlaceholderText('session.password');
const confirmPasswordInput = getByPlaceholderText('session.confirm_password');
const showPasswordButtons = getAllByTestId(document.body, 'show-confirm-password-button');

fireEvent.click(showPasswordButtons[0]);
fireEvent.click(showPasswordButtons[1]);

expect(passwordInput.getAttribute('type')).toBe('text');
expect(confirmPasswordInput.getAttribute('type')).toBe('text');
});

it('submits form data correctly', async () => {
const axiosMock = jest.spyOn(axios, 'post');
axiosMock.mockResolvedValueOnce({ status: 202 }); // Accepted status code

// Render the Signup component
const { getByPlaceholderText, getByText } = render(<MemoryRouter><Signup /></MemoryRouter>);

// Get form elements and submit button by their text and placeholder values
const emailInput = getByPlaceholderText('session.email');
const usernameInput = getByPlaceholderText('session.username');
const passwordInput = getByPlaceholderText('session.password');
const signUpButton = getByTestId(document.body, 'Sign up');

// Fill out the form with valid data and submit it
fireEvent.change(emailInput, { target: { value: '[email protected]' } });
fireEvent.change(usernameInput, { target: { value: 'testuser' } });
fireEvent.change(passwordInput, { target: { value: 'password' } });
fireEvent.click(signUpButton);

// Check if the form data was sent correctly
await waitFor(() => {
expect(axiosMock).toHaveBeenCalledWith(process.env.API_URL, {
email: '[email protected]',
username: 'testuser',
password: 'password'
});
expect(axiosMock).toHaveBeenCalledTimes(1);
});

axiosMock.mockRestore();
});
});

0 comments on commit b2d0b56

Please sign in to comment.