mirror of
https://github.com/bitwarden/browser
synced 2025-12-18 17:23:37 +00:00
This PR - Moves the `compareInputs` validator to `libs/auth` (with some minor updates to the validator) - Adds unit tests for `compareInputs` - Removes the deprecated input validators from `InputsFieldMatch` along with the `inputs-field-match.validator.ts` file
242 lines
6.6 KiB
TypeScript
242 lines
6.6 KiB
TypeScript
import { FormControl, FormGroup, ValidationErrors } from "@angular/forms";
|
|
|
|
import { compareInputs, ValidationGoal } from "./compare-inputs.validator";
|
|
|
|
describe("compareInputs", () => {
|
|
let validationErrorsObj: ValidationErrors;
|
|
|
|
beforeEach(() => {
|
|
// Use a fresh object for each test so that a mutation in one test doesn't affect another test
|
|
validationErrorsObj = {
|
|
compareInputsError: {
|
|
message: "Custom error message",
|
|
},
|
|
};
|
|
});
|
|
|
|
it("should throw an error if compareInputs is not being applied to a FormGroup", () => {
|
|
// Arrange
|
|
const notAFormGroup = new FormControl("form-control");
|
|
|
|
// Act
|
|
const validatorFn = compareInputs(
|
|
ValidationGoal.InputsShouldMatch,
|
|
"ctrlA",
|
|
"ctrlB",
|
|
"Custom error message",
|
|
);
|
|
|
|
// Assert
|
|
expect(() => validatorFn(notAFormGroup)).toThrow(
|
|
"compareInputs only supports validation at the FormGroup level",
|
|
);
|
|
});
|
|
|
|
it("should throw an error if either control is not found", () => {
|
|
// Arrange
|
|
const formGroupMissingControl = new FormGroup({
|
|
ctrlA: new FormControl("content"),
|
|
});
|
|
|
|
// Act
|
|
const validatorFn = compareInputs(
|
|
ValidationGoal.InputsShouldMatch,
|
|
"ctrlA",
|
|
"ctrlB", // ctrlB is missing above
|
|
"Custom error message",
|
|
);
|
|
|
|
// Assert
|
|
expect(() => validatorFn(formGroupMissingControl)).toThrow(
|
|
"[compareInputs validator] one or both of the specified controls could not be found in the form group",
|
|
);
|
|
});
|
|
|
|
it("should throw an error if the name of one of the form controls is incorrect or mispelled", () => {
|
|
// Arrange
|
|
const formGroupMissingControl = new FormGroup({
|
|
ctrlA: new FormControl("content"),
|
|
ctrlB: new FormControl("content"),
|
|
});
|
|
|
|
// Act
|
|
const validatorFn = compareInputs(
|
|
ValidationGoal.InputsShouldMatch,
|
|
"ctrlA",
|
|
"ctrlC", // ctrlC is incorrect (mimics a developer misspelling a form control name)
|
|
"Custom error message",
|
|
);
|
|
|
|
// Assert
|
|
expect(() => validatorFn(formGroupMissingControl)).toThrow(
|
|
"[compareInputs validator] one or both of the specified controls could not be found in the form group",
|
|
);
|
|
});
|
|
|
|
it("should return null if both controls have empty string values", () => {
|
|
// Arrange
|
|
const formGroup = new FormGroup({
|
|
ctrlA: new FormControl(""),
|
|
ctrlB: new FormControl(""),
|
|
});
|
|
|
|
// Act
|
|
const validatorFn = compareInputs(
|
|
ValidationGoal.InputsShouldMatch,
|
|
"ctrlA",
|
|
"ctrlB",
|
|
"Custom error message",
|
|
);
|
|
|
|
const result = validatorFn(formGroup);
|
|
|
|
// Assert
|
|
expect(result).toBeNull();
|
|
});
|
|
|
|
it("should call setErrors() on ctrlB if validation fails", () => {
|
|
// Arrange
|
|
const formGroup = new FormGroup({
|
|
ctrlA: new FormControl("apple"),
|
|
ctrlB: new FormControl("banana"),
|
|
});
|
|
|
|
const ctrlBSetErrorsSpy = jest.spyOn(formGroup.controls.ctrlB, "setErrors");
|
|
|
|
// Act
|
|
const validatorFn = compareInputs(
|
|
ValidationGoal.InputsShouldMatch,
|
|
"ctrlA",
|
|
"ctrlB",
|
|
"Custom error message",
|
|
);
|
|
|
|
validatorFn(formGroup);
|
|
|
|
// Assert
|
|
expect(ctrlBSetErrorsSpy).toHaveBeenCalledWith(validationErrorsObj);
|
|
});
|
|
|
|
it("should call setErrors() on ctrlA if validation fails and 'showErrorOn' is set to 'controlA'", () => {
|
|
// Arrange
|
|
const formGroup = new FormGroup({
|
|
ctrlA: new FormControl("apple"),
|
|
ctrlB: new FormControl("banana"),
|
|
});
|
|
|
|
const ctrlASetErrorsSpy = jest.spyOn(formGroup.controls.ctrlA, "setErrors");
|
|
const ctrlBSetErrorsSpy = jest.spyOn(formGroup.controls.ctrlB, "setErrors");
|
|
|
|
// Act
|
|
const validatorFn = compareInputs(
|
|
ValidationGoal.InputsShouldMatch,
|
|
"ctrlA",
|
|
"ctrlB",
|
|
"Custom error message",
|
|
"controlA",
|
|
);
|
|
|
|
validatorFn(formGroup);
|
|
|
|
// Assert
|
|
expect(ctrlASetErrorsSpy).toHaveBeenCalledWith(validationErrorsObj);
|
|
expect(ctrlBSetErrorsSpy).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("should not call setErrors() on ctrlB if validation passes and there is not a pre-existing error on ctrlB", () => {
|
|
// Arrange
|
|
const formGroup = new FormGroup({
|
|
ctrlA: new FormControl("apple"),
|
|
ctrlB: new FormControl("apple"),
|
|
});
|
|
|
|
const ctrlBSetErrorsSpy = jest.spyOn(formGroup.controls.ctrlB, "setErrors");
|
|
|
|
// Act
|
|
const validatorFn = compareInputs(
|
|
ValidationGoal.InputsShouldMatch,
|
|
"ctrlA",
|
|
"ctrlB",
|
|
"Custom error message",
|
|
);
|
|
|
|
validatorFn(formGroup);
|
|
|
|
// Assert
|
|
expect(ctrlBSetErrorsSpy).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("should call setErrors(null) on ctrlB if validation passes and there is a pre-existing error on ctrlB", () => {
|
|
// Arrange
|
|
const formGroup = new FormGroup({
|
|
ctrlA: new FormControl("apple"),
|
|
ctrlB: new FormControl("apple"),
|
|
});
|
|
|
|
const ctrlBSetErrorsSpy = jest.spyOn(formGroup.controls.ctrlB, "setErrors");
|
|
|
|
formGroup.controls.ctrlB.setErrors(validationErrorsObj); // the pre-existing error
|
|
|
|
// Act
|
|
const validatorFn = compareInputs(
|
|
ValidationGoal.InputsShouldMatch,
|
|
"ctrlA",
|
|
"ctrlB",
|
|
"Custom error message",
|
|
);
|
|
|
|
validatorFn(formGroup);
|
|
|
|
// Assert
|
|
expect(ctrlBSetErrorsSpy).toHaveBeenCalledWith(null);
|
|
});
|
|
|
|
const cases = [
|
|
{
|
|
expected: null,
|
|
goal: ValidationGoal.InputsShouldMatch,
|
|
matchStatus: "match",
|
|
values: { ctrlA: "apple", ctrlB: "apple" },
|
|
},
|
|
{
|
|
expected: "a ValidationErrors object",
|
|
goal: ValidationGoal.InputsShouldMatch,
|
|
matchStatus: "do not match",
|
|
values: { ctrlA: "apple", ctrlB: "banana" },
|
|
},
|
|
{
|
|
expected: null,
|
|
goal: ValidationGoal.InputsShouldNotMatch,
|
|
matchStatus: "do not match",
|
|
values: { ctrlA: "apple", ctrlB: "banana" },
|
|
},
|
|
{
|
|
expected: "a ValidationErrors object",
|
|
goal: ValidationGoal.InputsShouldNotMatch,
|
|
matchStatus: "match",
|
|
values: { ctrlA: "apple", ctrlB: "apple" },
|
|
},
|
|
];
|
|
|
|
cases.forEach(({ goal, expected, matchStatus, values }) => {
|
|
const goalString =
|
|
goal === ValidationGoal.InputsShouldMatch ? "InputsShouldMatch" : "InputsShouldNotMatch";
|
|
|
|
it(`should return ${expected} if the goal is ${goalString} and the inputs ${matchStatus}`, () => {
|
|
// Arrange
|
|
const formGroup = new FormGroup({
|
|
ctrlA: new FormControl(values.ctrlA),
|
|
ctrlB: new FormControl(values.ctrlB),
|
|
});
|
|
|
|
// Act
|
|
const validatorFn = compareInputs(goal, "ctrlA", "ctrlB", "Custom error message");
|
|
|
|
const result = validatorFn(formGroup);
|
|
|
|
// Assert
|
|
expect(result).toEqual(expected === null ? null : validationErrorsObj);
|
|
});
|
|
});
|
|
});
|