Skip to content

Commit 75b26c0

Browse files
committed
frontend/Register: Fix validation state updates
1 parent b981b76 commit 75b26c0

File tree

2 files changed

+124
-6
lines changed

2 files changed

+124
-6
lines changed

frontend/src/components/Register.tsx

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ interface RegisterSchema {
2424
interface RegisterState {
2525
accepted: boolean,
2626
password: string,
27+
passwordValid: boolean,
2728
confirmPassword: string,
2829
confirmPasswordValid: boolean,
2930
name: string,
@@ -46,6 +47,7 @@ export class Register extends Component<{}, RegisterState> {
4647
this.state = {
4748
accepted: false,
4849
password: "",
50+
passwordValid: true,
4951
confirmPassword: "",
5052
confirmPasswordValid: true,
5153
name: "",
@@ -72,6 +74,13 @@ export class Register extends Component<{}, RegisterState> {
7274

7375
const state = this.state as RegisterState;
7476

77+
if (this.state.password.length < 8) {
78+
state.passwordValid = false;
79+
res = false;
80+
} else {
81+
state.passwordValid = true;
82+
}
83+
7584
const passwordsMatch = this.state.password === this.state.confirmPassword;
7685
if (!passwordsMatch) {
7786
state.confirmPasswordValid = false;
@@ -211,7 +220,11 @@ export class Register extends Component<{}, RegisterState> {
211220
<Form.Group className="mb-3" controlId="registerName">
212221
<Form.Label>{t("name")}</Form.Label>
213222
<Form.Control name="Name" type="text" placeholder="John Doe" value={this.state.name} isInvalid={!this.state.nameValid} onChange={(e) => {
214-
this.setState({name: (e.target as HTMLInputElement).value})
223+
const newName = (e.target as HTMLInputElement).value;
224+
this.setState({
225+
name: newName,
226+
nameValid: newName.length > 0
227+
});
215228
}} />
216229
<Form.Control.Feedback type="invalid">
217230
{t("name_error_message")}
@@ -220,7 +233,11 @@ export class Register extends Component<{}, RegisterState> {
220233
<Form.Group className="mb-3" controlId="registerEmail">
221234
<Form.Label>{t("email")}</Form.Label>
222235
<Form.Control type="email" placeholder={t("email")} value={this.state.email} isInvalid={!this.state.emailValid} onChange={(e) => {
223-
this.setState({email: (e.target as HTMLInputElement).value})
236+
const newEmail = (e.target as HTMLInputElement).value;
237+
this.setState({
238+
email: newEmail,
239+
emailValid: newEmail.length > 0
240+
});
224241
}} />
225242
<Form.Control.Feedback type="invalid">
226243
{t("email_error_message")}
@@ -233,12 +250,12 @@ export class Register extends Component<{}, RegisterState> {
233250
showStrength={true}
234251
onChange={(e) => {
235252
this.setState({password: e}, async () => {
236-
if (!this.state.confirmPasswordValid) {
253+
if (!this.state.confirmPasswordValid || !this.state.passwordValid) {
237254
await this.checkPassword();
238255
}
239256
});
240257
}}
241-
isInvalid={this.state.password.length < 8}
258+
isInvalid={!this.state.passwordValid}
242259
invalidMessage={t("password_error_message")} />
243260
</Form.Group>
244261
<Form.Group className="mb-3" controlId="registerConfirmPassword">
@@ -252,10 +269,22 @@ export class Register extends Component<{}, RegisterState> {
252269
}}
253270
invalidMessage={t("confirm_password_error_message")} />
254271
</Form.Group>
255-
<Form.Group className="mb-3" onClick={() => this.setState({acceptPrivacyChecked: !this.state.acceptPrivacyChecked})}>
272+
<Form.Group className="mb-3" onClick={() => {
273+
const newChecked = !this.state.acceptPrivacyChecked;
274+
this.setState({
275+
acceptPrivacyChecked: newChecked,
276+
acceptPrivacyValid: newChecked
277+
});
278+
}}>
256279
<Form.Check checked={this.state.acceptPrivacyChecked} type="checkbox" label={<Trans i18nKey="register.accept_privacy_notice" ><a target="__blank" href={privacy_notice}>link</a></Trans>} isInvalid={!this.state.acceptPrivacyValid} />
257280
</Form.Group>
258-
<Form.Group className="mb-3" onClick={() => this.setState({termsAndConditionsChecked: !this.state.termsAndConditionsChecked})}>
281+
<Form.Group className="mb-3" onClick={() => {
282+
const newChecked = !this.state.termsAndConditionsChecked;
283+
this.setState({
284+
termsAndConditionsChecked: newChecked,
285+
termsAndConditionsValid: newChecked
286+
});
287+
}}>
259288
<Form.Check checked={this.state.termsAndConditionsChecked} type="checkbox" label={<Trans i18nKey="register.accept_terms_and_conditions" ><a target="__blank" href={terms_of_use}>link</a></Trans>} isInvalid={!this.state.termsAndConditionsValid} />
260289
</Form.Group>
261290
<Button variant="primary" type="submit">

frontend/src/components/__tests__/register.test.tsx

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,4 +436,93 @@ describe('Register Component', () => {
436436
expect(confirmPasswordInput).not.toHaveClass('invalid');
437437
});
438438
});
439+
440+
it('dynamically removes errors when correct values are entered in all fields', async () => {
441+
render(<Register />);
442+
443+
// First, submit the form with all empty fields to trigger all validation errors
444+
const submitButton = screen.getByTestId('submit-button');
445+
fireEvent.click(submitButton);
446+
447+
// Wait for all validation errors to appear
448+
await waitFor(() => {
449+
expect(screen.getByRole('textbox', { name: 'name' })).toHaveClass('invalid');
450+
expect(screen.getByRole('textbox', { name: 'email' })).toHaveClass('invalid');
451+
expect(screen.getByRole('textbox', { name: 'password' })).toHaveClass('invalid');
452+
expect(screen.getAllByRole('checkbox')[0]).toHaveClass('invalid');
453+
expect(screen.getAllByRole('checkbox')[1]).toHaveClass('invalid');
454+
});
455+
456+
// Get form inputs
457+
const nameInput = screen.getByRole('textbox', { name: 'name' });
458+
const emailInput = screen.getByRole('textbox', { name: 'email' });
459+
const passwordInput = screen.getByRole('textbox', { name: 'password' });
460+
const confirmPasswordInput = screen.getByRole('textbox', { name: 'confirm_password' });
461+
const checkboxes = screen.getAllByRole('checkbox');
462+
463+
// Test 1: Enter invalid password (too short) - should still show error
464+
fireEvent.change(passwordInput, { target: { value: 'short' } });
465+
fireEvent.change(confirmPasswordInput, { target: { value: 'short' } });
466+
fireEvent.click(submitButton);
467+
468+
await waitFor(() => {
469+
expect(passwordInput).toHaveClass('invalid');
470+
});
471+
472+
// Test 2: Enter valid password and confirm - errors should be removed
473+
fireEvent.change(passwordInput, { target: { value: 'ValidPass123!' } });
474+
475+
await waitFor(() => {
476+
expect(passwordInput).not.toHaveClass('invalid');
477+
});
478+
479+
// Test 3: Enter mismatched confirm password - should show error
480+
fireEvent.change(confirmPasswordInput, { target: { value: 'Different123!' } });
481+
482+
await waitFor(() => {
483+
expect(confirmPasswordInput).toHaveClass('invalid');
484+
});
485+
486+
// Test 4: Match confirm password - error should be removed
487+
fireEvent.change(confirmPasswordInput, { target: { value: 'ValidPass123!' } });
488+
489+
await waitFor(() => {
490+
expect(confirmPasswordInput).not.toHaveClass('invalid');
491+
});
492+
493+
// Test 5: Enter valid name - error should be removed
494+
fireEvent.change(nameInput, { target: { value: 'John Doe' } });
495+
496+
await waitFor(() => {
497+
expect(nameInput).not.toHaveClass('invalid');
498+
});
499+
500+
// Test 6: Enter valid email - error should be removed
501+
fireEvent.change(emailInput, { target: { value: '[email protected]' } });
502+
503+
await waitFor(() => {
504+
expect(emailInput).not.toHaveClass('invalid');
505+
});
506+
507+
// Test 7: Check privacy policy - error should be removed
508+
fireEvent.click(checkboxes[0]);
509+
510+
await waitFor(() => {
511+
expect(checkboxes[0]).not.toHaveClass('invalid');
512+
});
513+
514+
// Test 8: Check terms and conditions - error should be removed
515+
fireEvent.click(checkboxes[1]);
516+
517+
await waitFor(() => {
518+
expect(checkboxes[1]).not.toHaveClass('invalid');
519+
});
520+
521+
// Final validation: All fields should now be valid and form should submit
522+
fireEvent.click(submitButton);
523+
524+
await waitFor(() => {
525+
expect(mockUtils.fetchClient.POST).toHaveBeenCalled();
526+
});
527+
});
439528
});

0 commit comments

Comments
 (0)