3

How to unit test (Jest here) custom validator, which has FormGroup? I've seen this question, but it's about FormControl.

The function to be tested.

import { FormGroup } from '@angular/forms';

/**
 * @description Validate if passwords are different.
 * @function validatePasswords
 * @param {string} passwordControlName - reference to formControlPassword of the contactForm.
 * @param {string} confirmPasswordControlName - reference to formControlConfirmPassword of the contactForm.
 * @returns {(formGroup: FormGroup) => void}
 */
export function validatePasswords(
  passwordControlName: string,
  confirmPasswordControlName: string
): (formGroup: FormGroup) => void {
  return (formGroup: FormGroup) => {
    // Get values of desired controls of the form.
    const passwordControl = formGroup.controls[passwordControlName];
    const confirmPasswordControl =
      formGroup.controls[confirmPasswordControlName];

    if (
      // Don't show the error if any different error occured in password confirmation.
      confirmPasswordControl.errors &&
      !confirmPasswordControl.errors.passwordMismatch
    ) {
      return; // Different validator shown an error, therefore return.
    }

    // Check if password and password confirmation are different.
    if (passwordControl.value !== confirmPasswordControl.value) {
      confirmPasswordControl.setErrors({ passwordMismatch: true }); // Password and confirm password are different, therefore show passwordMismatch error.
    } else {
      confirmPasswordControl.setErrors(null); // Password and confirm password are the same, therefore don't display any error.
    }
  };
}
Daniel Danielecki
  • 8,508
  • 6
  • 68
  • 94

1 Answers1

4

You can create a test FormGroup which consists of 2 password controls and apply validatePasswords validator to this group:

describe('validatePasswords', () => {
  const password = 'password';
  const confirmPassword = 'confirmPassword';
  let formGroup: FormGroup;

  beforeEach(() => {
    formGroup = new FormGroup({
      [password]: new FormControl(),
      [confirmPassword]: new FormControl(),
    }, validatePasswords(password, confirmPassword));
  });

  it('should has "passwordMismatch" error if passwords do not match', () => {
    formGroup.patchValue({
      [password]: '123',
      [confirmPassword]: '321'
    });
    
    expect(formGroup.get(confirmPassword).hasError('passwordMismatch')).toBe(true);
  });
  
  it('should be valid if passwords match', () => {
    formGroup.patchValue({
      [password]: '123',
      [confirmPassword]: '123'
    });
    
    expect(formGroup.get(confirmPassword).valid).toBe(true);
  });
});
vadimk7
  • 6,559
  • 1
  • 12
  • 15
  • In my `validatePasswords` function I've had to change the `TypeScript` typing from `: (formGroup: FormGroup) => void` to `: (formGroup: FormGroup) => void | any`. – Daniel Danielecki Mar 28 '21 at 20:02
  • @DanielDanielecki, yep, it's better to use `(formGroup: FormGroup) => ValidationErrors | null` as it's used in the default angular validators. – vadimk7 Mar 29 '21 at 06:13