diff --git a/client/src/App.tsx b/client/src/App.tsx index ad178d1..4a96aa8 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -13,6 +13,9 @@ import Landing from './views/Landing'; import Login from './views/Login'; import NotFound from './views/NotFound'; import Register from './views/Register'; +import EmailConfirmation from './views/EmailConfirmation'; +import ResetPassword from './views/ResetPassword'; +import CompleteResetPassword from './views/CompleteResetPassword'; import './styles/custom.scss'; /** @@ -49,6 +52,19 @@ function App(): React.ReactNode { path: '/home', element: }, + { + path: '/email-confirmation', + element: + }, + { + // The reset-password is where the password reset workflow starts + path: '/reset-password', + element: + }, + { + path: '/finish-reset-password', + element: + }, { path: '*', element: diff --git a/client/src/__test__/components/LoginForm.test.tsx b/client/src/__test__/components/LoginForm.test.tsx index cddad49..57347be 100644 --- a/client/src/__test__/components/LoginForm.test.tsx +++ b/client/src/__test__/components/LoginForm.test.tsx @@ -1,17 +1,46 @@ import React from 'react'; -import { describe, it, expect } from 'vitest'; +import { describe, it, expect, vi, beforeEach } from 'vitest'; import { BrowserRouter } from 'react-router'; -import { render, screen } from '@testing-library/react'; +import { fireEvent, render, screen } from '@testing-library/react'; import LoginForm from '../../components/LoginForm'; import '../../i18n'; +import AuthContext from '../../context/AuthContext'; + +const authContextMock = { + signed: true, + user: { + userId: 1, + name: 'Ricardo', + email: 'test@example.com', + admin: false, + createdAt: new Date(), + gravatarImageUrl: 'http://image.com' + }, + checkCurrentAuthUser: vi.fn(), + signIn: vi.fn(), + signOut: vi.fn(), + register: vi.fn(), + isAdmin: false, + updateUser: vi.fn() +}; describe('LoginForm Component test', () => { - it('should render the Login Form component correctly', () => { - render( + beforeEach(() => { + vi.resetAllMocks(); + }); + + const renderFn = (prefix: string) => { + return render( - + + + ); + }; + + it('should render the Login Form component correctly', () => { + renderFn("login"); const loginEmailInput: HTMLInputElement = screen.getByTestId('login_email_input'); expect(loginEmailInput).toBeDefined(); @@ -19,14 +48,90 @@ describe('LoginForm Component test', () => { }); it('should render the Register Form component correctly', () => { - render( - - - - ); + renderFn("register"); const loginEmailInput: HTMLInputElement = screen.getByTestId('register_email_input'); expect(loginEmailInput).toBeDefined(); expect(loginEmailInput.required).toBe(true); }); + + it('should render the Login Form correctly for "login" prefix', () => { + renderFn("login"); + + const emailInput = screen.getByTestId('login_email_input'); + const passwordInput = screen.getByTestId('account-password-login'); + const submitButton = screen.getByRole('button', { name: /login/i }); + + expect(emailInput).toBeDefined(); + expect(passwordInput).toBeDefined(); + expect(submitButton).toBeDefined(); + }); + + it('should render the Register Form correctly for "register" prefix', () => { + renderFn("register"); + + const emailInput = screen.getByTestId('register_email_input'); + const passwordInput = screen.getByTestId('account-password-login'); + const repeatPasswordInput = screen.getByTestId('account-repeat-password-register'); + const submitButton = screen.getByRole('button', { name: /create account/i }); + + expect(emailInput).toBeDefined(); + expect(passwordInput).toBeDefined(); + expect(repeatPasswordInput).toBeDefined(); + expect(submitButton).toBeDefined(); + }); + + it('should render the Reset Password Form correctly for "reset" prefix', () => { + renderFn("reset"); + + const emailInput = screen.getByTestId('reset_email_input'); + const submitButton = screen.getByRole('button', { name: /Send confirmation email/i }); + + expect(emailInput).toBeDefined(); + expect(submitButton).toBeDefined(); + }); + + it('should display error message when form is invalid', () => { + renderFn("login"); + + const submitButton = screen.getByRole('button', { name: /login/i }); + fireEvent.click(submitButton); + + const errorMessage = screen.getByText(/please fill in your username and password/i); + expect(errorMessage).toBeDefined(); + }); + + it('should display success message for "register" prefix after successful submission', async () => { + renderFn("register"); + + const emailInput = screen.getByTestId('register_email_input'); + const passwordInput = screen.getByTestId('account-password-login'); + const repeatPasswordInput = screen.getByTestId('account-repeat-password-register'); + const submitButton = screen.getByRole('button', { name: /create account/i }); + + fireEvent.change(emailInput, { target: { value: 'test@example.com' } }); + fireEvent.change(passwordInput, { target: { value: 'password123' } }); + fireEvent.change(repeatPasswordInput, { target: { value: 'password123' } }); + fireEvent.click(submitButton); + + const successMessage = await screen.findByText(/please confirm your email before proceeding/i); + expect(successMessage).toBeDefined(); + }); + + it('should disable resend button and show countdown for "register" prefix', async () => { + renderFn("register"); + + const emailInput = screen.getByTestId('register_email_input'); + const passwordInput = screen.getByTestId('account-password-login'); + const repeatPasswordInput = screen.getByTestId('account-repeat-password-register'); + const submitButton = screen.getByRole('button', { name: /create account/i }); + + fireEvent.change(emailInput, { target: { value: 'test@example.com' } }); + fireEvent.change(passwordInput, { target: { value: 'password123' } }); + fireEvent.change(repeatPasswordInput, { target: { value: 'password123' } }); + fireEvent.click(submitButton); + + const resendButton = screen.queryByRole('button', { name: /resend confirmation email/i }) as HTMLButtonElement; + expect(resendButton).toBeNull(); + }); }); diff --git a/client/src/__test__/components/NoteTitle.test.tsx b/client/src/__test__/components/NoteTitle.test.tsx index 48df10c..817eb45 100644 --- a/client/src/__test__/components/NoteTitle.test.tsx +++ b/client/src/__test__/components/NoteTitle.test.tsx @@ -5,7 +5,7 @@ import NoteTitle from '../../components/NoteTitle'; describe('NoteTitle test cases', () => { it('should render the note title', () => { - const { getByText } = render() + const { getByText } = render() expect(getByText('Test Title')).toBeDefined(); }); diff --git a/client/src/__test__/context/AuthProvider.test.tsx b/client/src/__test__/context/AuthProvider.test.tsx index 57af514..8b4d9ee 100644 --- a/client/src/__test__/context/AuthProvider.test.tsx +++ b/client/src/__test__/context/AuthProvider.test.tsx @@ -38,7 +38,7 @@ const ConsumerComponent: React.FC = () => { + + + )} + + {successMessage.length > 1 && prefix !== 'register' && ( + + { successMessage } + + )} +
- - {t(`${prefix}_email_label`)} - ) => { + setEmail(e.target.value); + }} /> - + )} - - {t(`${prefix}_password_label`)} - ) => { + setPassword(e.target.value); + }} + data_testid="account-password-login" /> - + )} + + {(prefix === 'register' || prefix === 'complete_reset') && ( + ) => { + setPasswordAgain(e.target.value); + }} + data_testid={`account-repeat-password-${prefix}`} + /> + )}