Skip to content

Commit

Permalink
refactor validation
Browse files Browse the repository at this point in the history
Add optional
  • Loading branch information
NarenderRajuB authored and nuwan-samarasinghe committed Feb 27, 2025
1 parent 8535548 commit a22e32d
Show file tree
Hide file tree
Showing 4 changed files with 184 additions and 2 deletions.
56 changes: 56 additions & 0 deletions runner/src/server/plugins/engine/components/EmailAddressField.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
//@ts-ignore
import {InputFieldsComponentsDef} from "@xgovformbuilder/model";
import {AdapterFormModel} from "../models";
import {
addClassOptionIfNone,
} from "../../../../../../digital-form-builder/runner/src/server/plugins/engine/components/helpers";
import {
EmailAddressField as XGovEmailAddressField
} from "../../../../../../digital-form-builder/runner/src/server/plugins/engine/components";
import Joi from "joi";

export class EmailAddressField extends XGovEmailAddressField {
private defaultMessage: string = "Enter an email address in the correct format, like [email protected]";
formSchema: Joi.StringSchema;
options: InputFieldsComponentsDef["options"];
schema: InputFieldsComponentsDef["schema"];

constructor(def: InputFieldsComponentsDef, model: AdapterFormModel) {
//@ts-ignore
super(def, model);

this.options = def.options;
this.schema = def.schema;
this.formSchema = Joi.string();

const isRequired = def.options.required ?? true;

if (isRequired) {
this.formSchema = this.formSchema.required();
} else {
this.formSchema = this.formSchema.allow("").allow(null).optional();
}

this.formSchema = this.formSchema
.label(def.title.toLowerCase()) // Label for error messages
.email({tlds: {allow: false}}) // Strict email format validation (without allowing specific TLDs)
.messages({
//@ts-ignore
"string.email": def.options?.customValidationMessage ?? this.defaultMessage, // Custom error message
});
addClassOptionIfNone(this.options, "govuk-input--width-20");

}

getFormSchemaKeys() {
return {
[this.name]: this.formSchema,
};
}

getStateSchemaKeys() {
return {
[this.name]: this.formSchema,
};
}
}
2 changes: 1 addition & 1 deletion runner/src/server/plugins/engine/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export {
export {Details} from "../../../../../../digital-form-builder/runner/src/server/plugins/engine/components/Details";
export {
EmailAddressField
} from "../../../../../../digital-form-builder/runner/src/server/plugins/engine/components/EmailAddressField";
} from "./EmailAddressField";
export {
FileUploadField
} from "../../../../../../digital-form-builder/runner/src/server/plugins/engine/components/FileUploadField";
Expand Down
2 changes: 1 addition & 1 deletion runner/src/server/plugins/engine/views/partials/form.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{% from "button/macro.njk" import govukButton %}
{% from "summary-list/macro.njk" import govukSummaryList -%}

<form method="post" enctype="multipart/form-data" autocomplete="on">
<form method="post" enctype="multipart/form-data" autocomplete="on" novalidate>
<input type="hidden" name="crumb" value="{{ crumb }}"/>

{% if page.isRepeatingFieldPageController and details %}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import * as Code from "@hapi/code";
import * as Lab from "@hapi/lab";
//@ts-ignore
import {InputFieldsComponentsDef} from "@xgovformbuilder/model";
// @ts-ignore
import {EmailAddressField} from "src/server/plugins/engine/components";
// @ts-ignore
import {AdapterFormModel} from "src/server/plugins/engine/models";
// @ts-ignore
import {TranslationLoaderService} from "src/server/services/TranslationLoaderService";

const lab = Lab.script();
exports.lab = lab;
const {expect} = Code;
const {beforeEach, suite, test} = lab;

suite("EmailAddress field", () => {
let model: AdapterFormModel;

beforeEach(() => {
const translationService: TranslationLoaderService = new TranslationLoaderService();
const translations = translationService.getTranslations();
model = {
options: {
translationEn: translations.en,
translationCy: translations.cy
}
} as AdapterFormModel;
});

test("should be required by default", () => {
const def: InputFieldsComponentsDef = {
name: "myComponent",
title: "My component",
type: "EmailAddressField",
hint: "a hint",
options: {},
schema: {},
};

const {formSchema} = new EmailAddressField(def, model);

expect(formSchema.describe().flags!["presence"]).to.equal("required");
});

test("should validate email", () => {
const def: InputFieldsComponentsDef = {
name: "myComponent",
title: "My component",
type: "EmailAddressField",
hint: "a hint",
options: {},
schema: {},
};

const {formSchema} = new EmailAddressField(def, model);

expect(formSchema.validate("[email protected]").error).to.be.undefined();
expect(formSchema.validate("test").error!.message).to.contain(
`Enter an email address in the correct format, like [email protected]`
);
expect(formSchema.validate("test@email").error!.message).to.contain(
`Enter an email address in the correct format, like [email protected]`
);
});

test("should display custom error message", () => {
const def: InputFieldsComponentsDef = {
name: "myComponent",
title: "My component",
type: "EmailAddressField",
hint: "a hint",
options: {
customValidationMessage: "Enter valid email address",
},
schema: {},
};

const {formSchema} = new EmailAddressField(def, model);

expect(formSchema.validate("www.gov.uk").error?.message).to.contain(
"Enter valid email address"
);
});

test("should be required by default", () => {
const def: InputFieldsComponentsDef = {
name: "myComponent",
title: "My component",
type: "EmailAddressField",
hint: "a hint",
options: {},
schema: {},
};

const {formSchema} = new EmailAddressField(def, model);

expect(formSchema.validate("").error?.message).to.contain(
`"my component" is not allowed to be empty`
);

expect(formSchema.validate(null).error?.message).to.contain(
`"my component" must be a string`
);
});

test("should allow empty strings and null values when not required", () => {
const def: InputFieldsComponentsDef = {
name: "myComponent",
title: "My component",
type: "EmailAddressField",
hint: "a hint",
options: {
required: false,
},
schema: {},
};

const {formSchema} = new EmailAddressField(def, model);

expect(formSchema.validate("").error).to.be.undefined();

expect(formSchema.validate(null).error).to.be.undefined();
});

});

0 comments on commit a22e32d

Please sign in to comment.