Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New tests, Create account page refactoring. #137

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions frontend/src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<>
<Routes>

{/* <Route path="/" element={isLoggedIn ? <Private Component={Home} /> : <LoginPage />} /> commented out for testing */}
{/* <Route path="/home" element={<Private Component={Home} />} /> */}
<Route path="/" element={isLoggedIn ? <Private Component={Home} /> : <LoginPage />} />
<Route path="/home" element={<Private Component={Home} />} />
<Route path="/" element={isLoggedIn ? <Home/> : <LoginPage />} />
<Route path="/login" element={<LoginPage />} />
<Route path="/signup" element={<CreateAccountPage />} />
<Route path="/forgot-password" element={<ForgotPasswordPage />} />
<Route path="/reset-password" element={<PasswordResetPage />} />
Expand Down
54 changes: 0 additions & 54 deletions frontend/src/components/ButtonGroup.jsx

This file was deleted.

105 changes: 59 additions & 46 deletions frontend/src/scenes/login/CreateAccountPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand Down Expand Up @@ -45,80 +46,92 @@ 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 (
<div className="login-container">
<h2>Create an account</h2>

{formFields.map(({ label, name, type, placeholder, isValid }) => (
<div className="form-group" key={name}>
<div className='check-div'>
{isValid && <CheckCircleIcon style={{ color: 'green', fontSize: '20px' }} />}
<label>{label}</label>
</div>
<input
type={type}
name={name}
value={formData[name]}
onChange={handleInputChange}
placeholder={placeholder}
/>
<div className="form-group">
<div className='check-div'>
{validation.isUsernameValid && <CheckCircleIcon style={{ color: 'green', fontSize: '20px' }} />}
<label htmlFor="username">Username*:</label>
</div>
))}
<input
id="username"
type="name"
name="username"
value={formData.username}
onChange={handleInputChange}
placeholder="Enter your username"
/>
{error && <div className="error-message">{error}</div>}
</div>

<div className="form-group">
<div className='check-div'>
{validation.isEmailValid && <CheckCircleIcon style={{ color: 'green', fontSize: '20px' }} />}
<label htmlFor="email">Email*:</label>
</div>
<input
id="email"
type="email"
name="email"
value={formData.email}
onChange={handleInputChange}
placeholder="Enter your email"
/>
</div>

<div className="form-group">
<div className='check-div'>
{validation.isPasswordValid && <CheckCircleIcon style={{ color: 'green', fontSize: '20px' }} />}
<label htmlFor="password">Password*:</label>
</div>
<input
id="password"
type="password"
name="password"
value={formData.password}
onChange={handleInputChange}
placeholder="Create your password"
/>
</div>

<div className="password-constraints">
<CheckCircleIcon style={{ color: passwordChecks.atLeastEightCharacters ? 'green' : '#D0D5DD', fontSize: '20px', marginRight: "5px" }} />
<CheckCircleIcon style={{ color: passwordChecks.atLeastEightCharacters ? 'green' : '#D0D5DD', fontSize: '20px', marginRight: '5px' }} />
Must be at least 8 characters
</div>

<div className="password-constraints">
<CheckCircleIcon style={{ color: passwordChecks.hasSpecialCharacter ? 'green' : '#D0D5DD', fontSize: '20px', marginRight: "5px" }} />
<CheckCircleIcon style={{ color: passwordChecks.hasSpecialCharacter ? 'green' : '#D0D5DD', fontSize: '20px', marginRight: '5px' }} />
Must contain one special character
</div>

<button className="create-account-button" onClick={handleSignUp}>
Get started
</button>

<GoogleSignInButton />

<div className="sign-up-link">
Already have an account? <a href="/">Log in</a>
</div>
</div>
);
}

export default CreateAccountPage;
export default CreateAccountPage;
Original file line number Diff line number Diff line change
@@ -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(<CustomTextField label="Test Label" />);
const textField = screen.getByLabelText(/test label/i);
expect(textField).toBeDefined();
describe('CustomTextField', () => {
it('renders the CustomTextField with default props', () => {
render(<CustomTextField labelText="Test Label" />);

expect(screen.getByText('Test Label')).not.toBeNull();
expect(screen.getByRole('textbox')).not.toBeNull();
});

it("should render the helper text when provided", () => {
render(<CustomTextField label="Test Label" helperText="Help text" />);
const helperText = screen.getByText(/help text/i);
expect(helperText).toBeDefined();
it('displays the correct value', () => {
render(<CustomTextField labelText="Test Label" value="Test Value" />);

expect(screen.getByDisplayValue('Test Value')).not.toBeNull();
});

it("should show an error message when error prop is true", () => {
render(
<CustomTextField
label="Test Label"
error
helperText="This is an error message"
/>
);
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(<CustomTextField labelText="Test Label" onChange={handleChange} />);

fireEvent.change(screen.getByRole('textbox'), { target: { value: 'New Value' } });
expect(handleChange).toHaveBeenCalledWith(expect.any(Object));
});

it('displays helper text when provided', () => {
render(<CustomTextField labelText="Test Label" helperText="Helper Text" />);

expect(screen.getByText('Helper Text')).not.toBeNull();
});

it("should render start adornment when provided", () => {
render(<CustomTextField label="Test Label" startAdornment="http://" />);
const startAdornment = screen.getByText(/http:\/\//i);
expect(startAdornment).toBeDefined();
it('renders with multiline and rows props', () => {
render(<CustomTextField labelText="Test Label" multiline={true} rows={4} />);

expect(screen.getByRole('textbox').getAttribute('rows')).toBe('4');
});

it("should render end adornment when provided", () => {
it('renders with startAdornment and endAdornment', () => {
render(
<CustomTextField
label="Test Label"
endAdornment={<button>Copy</button>}
labelText="Test Label"
startAdornment={<div>Start</div>}
endAdornment={<div>End</div>}
/>
);
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(<CustomTextField label="Test Label" chips={chips} />);
const chip = screen.getByText(/design/i);
expect(chip).toBeDefined();
it('renders with chips', () => {
const chips = [{ label: 'Chip 1' }, { label: 'Chip 2' }];
render(<CustomTextField labelText="Test Label" chips={chips} />);

expect(screen.getByText('Chip 1')).not.toBeNull();
expect(screen.getByText('Chip 2')).not.toBeNull();
});

// Additional tests
it('renders with a placeholder', () => {
render(<CustomTextField labelText="Test Label" placeholder="Enter text here" />);

expect(screen.getByPlaceholderText('Enter text here')).not.toBeNull();
});

it("should allow the user to enter text", () => {
render(<CustomTextField label="Test Label" />);
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(<CustomTextField labelText="Test Label" inputHeight="50px" />);

expect(screen.getByRole('textbox').style.height).toBe('50px');
});

it('renders with custom label font weight', () => {
render(<CustomTextField labelText="Test Label" labelFontWeight={700} />);

const label = screen.getByText('Test Label');
expect(window.getComputedStyle(label).fontWeight).toBe('700');
});

it('renders with no chips when chips prop is empty', () => {
render(<CustomTextField labelText="Test Label" chips={[]} />);

expect(screen.queryByText('Chip 1')).toBeNull();
expect(screen.queryByText('Chip 2')).toBeNull();
});
});
Loading