diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index efa5ff6f..ddd6c9f2 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -18,15 +18,15 @@ import HintDefaultPage from "./scenes/hints/HintDefaultPage"; import CreatePopupPage from "./scenes/popup/CreatePopupPage"; function App() { - // const { isLoggedIn } = useAuth(); //commented out for testing - const isLoggedIn = true; + const { isLoggedIn } = useAuth(); //commented out for testing + // const isLoggedIn = true; return ( <> - - {/* : } /> commented out for testing */} - {/* } /> */} + : } /> + } /> : } /> + } /> } /> } /> } /> diff --git a/frontend/src/components/ButtonGroup.jsx b/frontend/src/components/ButtonGroup.jsx deleted file mode 100644 index 8bbced21..00000000 --- a/frontend/src/components/ButtonGroup.jsx +++ /dev/null @@ -1,54 +0,0 @@ -import * as React from 'react'; -import Button from '@mui/material/Button'; -import ButtonGroup from '@mui/material/ButtonGroup'; -import Box from '@mui/material/Box'; -import ArrowForwardIcon from '@mui/icons-material/ArrowForward'; -import ArrowBackIcon from '@mui/icons-material/ArrowBack'; -import AddIcon from '@mui/icons-material/Add'; - -export default function VariantButtonGroup() { - return ( - *': { - m: 1, - }, - }} - > - - - - - - - - - - - - ); -} \ No newline at end of file diff --git a/frontend/src/scenes/login/CreateAccountPage.jsx b/frontend/src/scenes/login/CreateAccountPage.jsx index 7c8c449c..6e53e323 100644 --- a/frontend/src/scenes/login/CreateAccountPage.jsx +++ b/frontend/src/scenes/login/CreateAccountPage.jsx @@ -9,6 +9,7 @@ function CreateAccountPage() { const [formData, setFormData] = useState({ username: '', email: '', password: '' }); const [validation, setValidation] = useState({ isUsernameValid: false, isEmailValid: false, isPasswordValid: false }); const [passwordChecks, setPasswordChecks] = useState({ hasSpecialCharacter: false, atLeastEightCharacters: false }); + const [error, setError] = useState(''); const navigate = useNavigate(); const handleInputChange = (e) => { @@ -45,75 +46,87 @@ function CreateAccountPage() { return; } + const userData = { username: formData.username, email: formData.email, password: formData.password }; + try { const response = await signUp(formData); console.log('Sign up successful:', response); navigate('/'); } catch (error) { - console.error('Sign up failed:', error); + if (error.response && error.response.data) { + if (error.response.data.error === 'User already exists') { + setError('User already exists'); + } else { + setError('An error occurred. Please try again.'); + } + } else { + setError('An error occurred. Please check your network connection and try again.'); + } } }; - const formFields = [ - { - label: 'Username*:', - name: 'username', - type: 'name', - placeholder: 'Enter your username', - isValid: validation.isUsernameValid - }, - { - label: 'Email*:', - name: 'email', - type: 'email', - placeholder: 'Enter your email', - isValid: validation.isEmailValid - }, - { - label: 'Password*:', - name: 'password', - type: 'password', - placeholder: 'Create your password', - isValid: validation.isPasswordValid - } - ]; - return (

Create an account

- - {formFields.map(({ label, name, type, placeholder, isValid }) => ( -
-
- {isValid && } - -
- +
+
+ {validation.isUsernameValid && } +
- ))} + + {error &&
{error}
} +
+ +
+
+ {validation.isEmailValid && } + +
+ +
+ +
+
+ {validation.isPasswordValid && } + +
+ +
- + Must be at least 8 characters
-
- + Must contain one special character
- -
Already have an account? Log in
@@ -121,4 +134,4 @@ function CreateAccountPage() { ); } -export default CreateAccountPage; +export default CreateAccountPage; \ No newline at end of file diff --git a/frontend/src/tests/components/textFieldComponent/CustomTextField.test.jsx b/frontend/src/tests/components/textFieldComponent/CustomTextField.test.jsx index 176ca896..a58342dd 100644 --- a/frontend/src/tests/components/textFieldComponent/CustomTextField.test.jsx +++ b/frontend/src/tests/components/textFieldComponent/CustomTextField.test.jsx @@ -1,62 +1,86 @@ -import { describe, it, expect } from "vitest"; -import { render, screen, fireEvent } from "@testing-library/react"; -import CustomTextField from "../../../components/TextFieldComponents/CustomTextField"; +import { render, screen, fireEvent } from '@testing-library/react'; +import { describe, it, expect } from 'vitest'; +import CustomTextField from '../../../components/TextFieldComponents/CustomTextField/CustomTextField'; -describe("CustomTextField", () => { - it("should render the text field with the provided label", () => { - render(); - const textField = screen.getByLabelText(/test label/i); - expect(textField).toBeDefined(); +describe('CustomTextField', () => { + it('renders the CustomTextField with default props', () => { + render(); + + expect(screen.getByText('Test Label')).not.toBeNull(); + expect(screen.getByRole('textbox')).not.toBeNull(); }); - it("should render the helper text when provided", () => { - render(); - const helperText = screen.getByText(/help text/i); - expect(helperText).toBeDefined(); + it('displays the correct value', () => { + render(); + + expect(screen.getByDisplayValue('Test Value')).not.toBeNull(); }); - it("should show an error message when error prop is true", () => { - render( - - ); - const errorMessage = screen.getByText(/This is an error message/i); - expect(errorMessage).toBeDefined(); - const styles = getComputedStyle(errorMessage); - expect(styles.color).toBe("rgb(211, 47, 47)"); + it('calls onChange when the input value changes', () => { + const handleChange = vi.fn(); + render(); + + fireEvent.change(screen.getByRole('textbox'), { target: { value: 'New Value' } }); + expect(handleChange).toHaveBeenCalledWith(expect.any(Object)); + }); + + it('displays helper text when provided', () => { + render(); + + expect(screen.getByText('Helper Text')).not.toBeNull(); }); - it("should render start adornment when provided", () => { - render(); - const startAdornment = screen.getByText(/http:\/\//i); - expect(startAdornment).toBeDefined(); + it('renders with multiline and rows props', () => { + render(); + + expect(screen.getByRole('textbox').getAttribute('rows')).toBe('4'); }); - it("should render end adornment when provided", () => { + it('renders with startAdornment and endAdornment', () => { render( Copy} + labelText="Test Label" + startAdornment={
Start
} + endAdornment={
End
} /> ); - const endAdornment = screen.getByText(/copy/i); - expect(endAdornment).toBeDefined(); + + expect(screen.getByText('Start')).not.toBeNull(); + expect(screen.getByText('End')).not.toBeNull(); }); - it("should render chips when provided", () => { - const chips = [{ label: "Design", onDelete: () => {} }]; - render(); - const chip = screen.getByText(/design/i); - expect(chip).toBeDefined(); + it('renders with chips', () => { + const chips = [{ label: 'Chip 1' }, { label: 'Chip 2' }]; + render(); + + expect(screen.getByText('Chip 1')).not.toBeNull(); + expect(screen.getByText('Chip 2')).not.toBeNull(); + }); + + // Additional tests + it('renders with a placeholder', () => { + render(); + + expect(screen.getByPlaceholderText('Enter text here')).not.toBeNull(); }); - it("should allow the user to enter text", () => { - render(); - const textField = screen.getByLabelText(/test label/i); - fireEvent.change(textField, { target: { value: "User input" } }); - expect(textField.value).toBe("User input"); + it('renders with custom input height', () => { + render(); + + expect(screen.getByRole('textbox').style.height).toBe('50px'); + }); + + it('renders with custom label font weight', () => { + render(); + + const label = screen.getByText('Test Label'); + expect(window.getComputedStyle(label).fontWeight).toBe('700'); + }); + + it('renders with no chips when chips prop is empty', () => { + render(); + + expect(screen.queryByText('Chip 1')).toBeNull(); + expect(screen.queryByText('Chip 2')).toBeNull(); }); }); diff --git a/frontend/src/tests/scenes/login/CreateAccountPage.test.jsx b/frontend/src/tests/scenes/login/CreateAccountPage.test.jsx new file mode 100644 index 00000000..88d18219 --- /dev/null +++ b/frontend/src/tests/scenes/login/CreateAccountPage.test.jsx @@ -0,0 +1,163 @@ +import { render, screen, fireEvent, act, waitFor } from '@testing-library/react'; +import { describe, it, expect, vi } from 'vitest'; +import { BrowserRouter as Router } from 'react-router-dom'; +import CreateAccountPage from '../../../scenes/login/CreateAccountPage'; +import { signUp } from '../../../services/loginServices'; + +vi.mock('../../../services/loginServices', () => ({ + signUp: vi.fn(), +})); + +describe('CreateAccountPage', () => { + it('renders the create account page', () => { + render( + + + + ); + + expect(screen.getByText('Create an account')).to.exist; + expect(screen.getByLabelText('Username*:')).to.exist; + expect(screen.getByLabelText('Email*:')).to.exist; + expect(screen.getByLabelText('Password*:')).to.exist; + expect(screen.getByText('Get started')).to.exist; + }); + + it('validates username input', () => { + render( + + + + ); + + const usernameInput = screen.getByPlaceholderText('Enter your username'); + fireEvent.change(usernameInput, { target: { value: 'testuser' } }); + + expect(usernameInput.value).to.equal('testuser'); + }); + + it('validates email input', () => { + render( + + + + ); + + const emailInput = screen.getByPlaceholderText('Enter your email'); + fireEvent.change(emailInput, { target: { value: 'test@example.com' } }); + + expect(emailInput.value).to.equal('test@example.com'); + }); + + it('validates password input', () => { + render( + + + + ); + + const passwordInput = screen.getByPlaceholderText('Create your password'); + fireEvent.change(passwordInput, { target: { value: 'Password1!' } }); + + expect(passwordInput.value).to.equal('Password1!'); + }); + + it('handles sign up success', async () => { + signUp.mockResolvedValueOnce({ data: { success: true } }); + + render( + + + + ); + + await act(async () => { + fireEvent.change(screen.getByPlaceholderText('Enter your username'), { target: { value: 'testuser' } }); + fireEvent.change(screen.getByPlaceholderText('Enter your email'), { target: { value: 'test@example.com' } }); + fireEvent.change(screen.getByPlaceholderText('Create your password'), { target: { value: 'Password1!' } }); + fireEvent.click(screen.getByText('Get started')); + }); + + expect(signUp).toHaveBeenCalledWith({ username: 'testuser', email: 'test@example.com', password: 'Password1!' }); + // Add more assertions as needed + }); + + it('handles sign up failure with user already exists error', async () => { + signUp.mockRejectedValueOnce({ + response: { + data: { error: 'User already exists' }, + status: 400, + }, + }); + + render( + + + + ); + + await act(async () => { + fireEvent.change(screen.getByPlaceholderText('Enter your username'), { target: { value: 'testuser' } }); + fireEvent.change(screen.getByPlaceholderText('Enter your email'), { target: { value: 'test@example.com' } }); + fireEvent.change(screen.getByPlaceholderText('Create your password'), { target: { value: 'Password1!' } }); + fireEvent.click(screen.getByText('Get started')); + }); + + await waitFor(() => { + expect(screen.getByText('User already exists')).to.exist; + }); + + expect(signUp).toHaveBeenCalledWith({ username: 'testuser', email: 'test@example.com', password: 'Password1!' }); + }); + + it('handles sign up failure with other errors', async () => { + signUp.mockRejectedValueOnce({ + response: { + data: { error: 'Some other error' }, + status: 500, + }, + }); + + render( + + + + ); + + await act(async () => { + fireEvent.change(screen.getByPlaceholderText('Enter your username'), { target: { value: 'testuser' } }); + fireEvent.change(screen.getByPlaceholderText('Enter your email'), { target: { value: 'test@example.com' } }); + fireEvent.change(screen.getByPlaceholderText('Create your password'), { target: { value: 'Password1!' } }); + fireEvent.click(screen.getByText('Get started')); + }); + + await waitFor(() => { + expect(screen.getByText('An error occurred. Please try again.')).to.exist; + }); + + expect(signUp).toHaveBeenCalledWith({ username: 'testuser', email: 'test@example.com', password: 'Password1!' }); + }); + + it('handles network errors gracefully', async () => { + signUp.mockRejectedValueOnce(new Error('Network Error')); + + render( + + + + ); + + await act(async () => { + fireEvent.change(screen.getByPlaceholderText('Enter your username'), { target: { value: 'testuser' } }); + fireEvent.change(screen.getByPlaceholderText('Enter your email'), { target: { value: 'test@example.com' } }); + fireEvent.change(screen.getByPlaceholderText('Create your password'), { target: { value: 'Password1!' } }); + fireEvent.click(screen.getByText('Get started')); + }); + + await waitFor(() => { + expect(screen.getByText('An error occurred. Please check your network connection and try again.')).to.exist; + }); + + expect(signUp).toHaveBeenCalledWith({ username: 'testuser', email: 'test@example.com', password: 'Password1!' }); + }); +}); diff --git a/frontend/src/tests/scenes/login/LoginPage.test.jsx b/frontend/src/tests/scenes/login/LoginPage.test.jsx index 2d80ca29..b49954c3 100644 --- a/frontend/src/tests/scenes/login/LoginPage.test.jsx +++ b/frontend/src/tests/scenes/login/LoginPage.test.jsx @@ -1,40 +1,55 @@ -import { describe, it, expect } from "vitest"; -import { render, screen, fireEvent } from "@testing-library/react"; -import LoginPage from "../../../scenes/login/LoginPage"; - -describe("LoginPage", () => { - it("should render the login form with email and password inputs", () => { - render(); - const emailInput = screen.getByLabelText(/email:/i); - const passwordInput = screen.getByLabelText(/password:/i); - expect(emailInput).toBeDefined(); - expect(passwordInput).toBeDefined(); - }); +import { render, screen, fireEvent } from '@testing-library/react'; +import { describe, it, expect, vi } from 'vitest'; +import { BrowserRouter as Router } from 'react-router-dom'; +import LoginPage from '../../../scenes/login/LoginPage'; +import * as loginServices from '../../../services/loginServices'; - it("should allow the user to enter an email", () => { - render(); - const emailInput = screen.getByLabelText(/email:/i); - fireEvent.change(emailInput, { target: { value: "test@example.com" } }); - expect(emailInput.value).toBe("test@example.com"); - }); +vi.mock('../../../services/loginServices'); + +describe('LoginPage', () => { + it('renders the login page', () => { + render( + + + + ); - it("should allow the user to enter a password", () => { - render(); - const passwordInput = screen.getByLabelText(/password:/i); - fireEvent.change(passwordInput, { target: { value: "password" } }); - expect(passwordInput.value).toBe("password"); + expect(screen.getByText('Log in to your account')).not.toBeNull(); + expect(screen.getByLabelText('Email:')).not.toBeNull(); + expect(screen.getByLabelText('Password:')).not.toBeNull(); + expect(screen.getByText("Don't have an account?")).not.toBeNull(); }); - it("should toggle the remember me checkbox", () => { - render(); - const rememberMeCheckbox = screen.getByLabelText(/remember for 30 days/i); - fireEvent.click(rememberMeCheckbox); - expect(rememberMeCheckbox.checked).toBe(true); + it('handles login success', async () => { + loginServices.login.mockResolvedValueOnce({ data: { success: true } }); + + render( + + + + ); + + fireEvent.change(screen.getByLabelText('Email:'), { target: { value: 'test@example.com' } }); + fireEvent.change(screen.getByLabelText('Password:'), { target: { value: 'password' } }); + fireEvent.click(screen.getByText('Sign in')); + + expect(loginServices.login).toHaveBeenCalledWith('test@example.com', 'password'); + // Add more assertions as needed }); - it("should have a sign-in button", () => { - render(); - const signInButton = screen.getByRole("button", { name: "Sign in" }); - expect(signInButton).toBeDefined(); + it('handles login failure', async () => { + loginServices.login.mockRejectedValueOnce({ response: { data: { error: 'Invalid credentials' } } }); + + render( + + + + ); + + fireEvent.change(screen.getByLabelText('Email:'), { target: { value: 'test@example.com' } }); + fireEvent.change(screen.getByLabelText('Password:'), { target: { value: 'wrongpassword' } }); + fireEvent.click(screen.getByText('Sign in')); + + expect(await screen.findByText('Invalid credentials')).not.toBeNull(); }); });