@finsys/core 1.0.1 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/catalogs.d.ts +13 -0
- package/dist/catalogs.js +35 -0
- package/dist/catalogs.test.d.ts +1 -0
- package/dist/catalogs.test.js +207 -0
- package/dist/data/form-field-base-specs.json +7353 -0
- package/dist/data/form-field-types.json +83 -0
- package/dist/data/form-field-validators.json +341 -0
- package/dist/form-field-category.d.ts +12 -0
- package/dist/form-field-category.js +21 -0
- package/dist/form-field-validator.d.ts +18 -0
- package/dist/form-field-validator.js +41 -0
- package/dist/form-field.d.ts +81 -0
- package/dist/form-field.js +197 -0
- package/dist/form-field.test.d.ts +1 -0
- package/dist/form-field.test.js +482 -0
- package/dist/form-spec.d.ts +39 -0
- package/dist/form-spec.js +278 -0
- package/dist/form-spec.test.d.ts +1 -0
- package/dist/form-spec.test.js +322 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +7 -0
- package/package.json +4 -2
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
export var FieldType;
|
|
2
|
+
(function (FieldType) {
|
|
3
|
+
FieldType["FILE"] = "file";
|
|
4
|
+
FieldType["TEXT"] = "text";
|
|
5
|
+
FieldType["DROPDOWN"] = "dropdown";
|
|
6
|
+
FieldType["BOOLEAN"] = "boolean";
|
|
7
|
+
FieldType["CHECKBOX"] = "checkbox";
|
|
8
|
+
FieldType["RADIOGROUP"] = "radiogroup";
|
|
9
|
+
FieldType["SLIDER"] = "slider";
|
|
10
|
+
FieldType["HTML"] = "html";
|
|
11
|
+
FieldType["TAGBOX"] = "tagbox";
|
|
12
|
+
FieldType["NUMBER"] = "number";
|
|
13
|
+
FieldType["EMAIL"] = "email";
|
|
14
|
+
FieldType["COMMENT"] = "comment";
|
|
15
|
+
FieldType["RANGE"] = "range";
|
|
16
|
+
FieldType["UNKNOWN"] = "unknown";
|
|
17
|
+
})(FieldType || (FieldType = {}));
|
|
18
|
+
function toFieldType(value) {
|
|
19
|
+
const typeMap = {
|
|
20
|
+
[FieldType.FILE]: FieldType.FILE,
|
|
21
|
+
[FieldType.TEXT]: FieldType.TEXT,
|
|
22
|
+
[FieldType.DROPDOWN]: FieldType.DROPDOWN,
|
|
23
|
+
[FieldType.BOOLEAN]: FieldType.BOOLEAN,
|
|
24
|
+
[FieldType.CHECKBOX]: FieldType.CHECKBOX,
|
|
25
|
+
[FieldType.RADIOGROUP]: FieldType.RADIOGROUP,
|
|
26
|
+
[FieldType.SLIDER]: FieldType.SLIDER,
|
|
27
|
+
[FieldType.HTML]: FieldType.HTML,
|
|
28
|
+
[FieldType.TAGBOX]: FieldType.TAGBOX,
|
|
29
|
+
[FieldType.NUMBER]: FieldType.NUMBER,
|
|
30
|
+
[FieldType.EMAIL]: FieldType.EMAIL,
|
|
31
|
+
[FieldType.COMMENT]: FieldType.COMMENT,
|
|
32
|
+
[FieldType.RANGE]: FieldType.RANGE,
|
|
33
|
+
};
|
|
34
|
+
return typeMap[value] || FieldType.UNKNOWN;
|
|
35
|
+
}
|
|
36
|
+
export default class FormField {
|
|
37
|
+
constructor(name, displayName, type, category) {
|
|
38
|
+
this.fieldObject = {
|
|
39
|
+
name,
|
|
40
|
+
displayName,
|
|
41
|
+
type,
|
|
42
|
+
category,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
static fromObject(fieldObject) {
|
|
46
|
+
if (!fieldObject.name) {
|
|
47
|
+
throw new Error('Field name is required');
|
|
48
|
+
}
|
|
49
|
+
if (!fieldObject.displayName || !fieldObject.category) {
|
|
50
|
+
throw new Error(`Invalid field object: '${fieldObject.name}' field missing displayName or category`);
|
|
51
|
+
}
|
|
52
|
+
const identifiedType = toFieldType(fieldObject.type);
|
|
53
|
+
if (identifiedType === FieldType.UNKNOWN) {
|
|
54
|
+
throw new Error(`'${fieldObject.name}' field has unsupported or missing type: ${fieldObject.type}`);
|
|
55
|
+
}
|
|
56
|
+
if (fieldObject.choices &&
|
|
57
|
+
!['dropdown', 'checkbox', 'radiogroup', 'tagbox'].includes(fieldObject.type)) {
|
|
58
|
+
// Logic remained as is
|
|
59
|
+
}
|
|
60
|
+
if (fieldObject.type === 'dropdown' && !fieldObject.choices) {
|
|
61
|
+
throw new Error(`Invalid field object: '${fieldObject.name}' field has type dropdown, but does not have choices`);
|
|
62
|
+
}
|
|
63
|
+
let field;
|
|
64
|
+
if (fieldObject.type === 'file') {
|
|
65
|
+
field = new FileFormField(fieldObject.name, fieldObject.displayName || fieldObject.name, fieldObject.type, String(fieldObject.category), fieldObject.ihs_column_names || []);
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
field = new BasicFormField(fieldObject.name, fieldObject.displayName || fieldObject.name, fieldObject.type || 'text', String(fieldObject.category));
|
|
69
|
+
}
|
|
70
|
+
field.fieldObject = { ...fieldObject };
|
|
71
|
+
field.fieldObject.category = String(field.fieldObject.category);
|
|
72
|
+
// Remove legacy categoryId property
|
|
73
|
+
delete field.fieldObject.categoryId;
|
|
74
|
+
return field;
|
|
75
|
+
}
|
|
76
|
+
cleanObject(obj) {
|
|
77
|
+
if (obj === null || obj === undefined)
|
|
78
|
+
return obj;
|
|
79
|
+
let target = obj;
|
|
80
|
+
if (typeof obj.toJSON === 'function' && obj !== this) {
|
|
81
|
+
target = obj.toJSON();
|
|
82
|
+
}
|
|
83
|
+
if (Array.isArray(target)) {
|
|
84
|
+
return target.map((v) => (typeof v === 'object' ? this.cleanObject(v) : v));
|
|
85
|
+
}
|
|
86
|
+
if (typeof target !== 'object')
|
|
87
|
+
return target;
|
|
88
|
+
const cleaned = {};
|
|
89
|
+
Object.keys(target).forEach((key) => {
|
|
90
|
+
const value = target[key];
|
|
91
|
+
if (value !== null && value !== undefined && value !== '') {
|
|
92
|
+
if (typeof value === 'object') {
|
|
93
|
+
cleaned[key] = this.cleanObject(value);
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
cleaned[key] = value;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
return cleaned;
|
|
101
|
+
}
|
|
102
|
+
toJSON() {
|
|
103
|
+
return this.cleanObject({ ...this.fieldObject });
|
|
104
|
+
}
|
|
105
|
+
get displayName() {
|
|
106
|
+
return this.fieldObject.displayName || this.fieldObject.name || '';
|
|
107
|
+
}
|
|
108
|
+
set displayName(newDisplayName) {
|
|
109
|
+
this.fieldObject.displayName = newDisplayName;
|
|
110
|
+
}
|
|
111
|
+
get name() {
|
|
112
|
+
return this.fieldObject.name || '';
|
|
113
|
+
}
|
|
114
|
+
get type() {
|
|
115
|
+
return toFieldType(this.fieldObject.type);
|
|
116
|
+
}
|
|
117
|
+
get inputType() {
|
|
118
|
+
return this.fieldObject.inputType ?? '';
|
|
119
|
+
}
|
|
120
|
+
get categoryId() {
|
|
121
|
+
return String(this.fieldObject.category || '');
|
|
122
|
+
}
|
|
123
|
+
set categoryId(categoryId) {
|
|
124
|
+
this.fieldObject.category = categoryId;
|
|
125
|
+
}
|
|
126
|
+
get defaultValue() {
|
|
127
|
+
return this.fieldObject.defaultValue;
|
|
128
|
+
}
|
|
129
|
+
set defaultValue(value) {
|
|
130
|
+
this.fieldObject.defaultValue = value;
|
|
131
|
+
}
|
|
132
|
+
get placeholder() {
|
|
133
|
+
return this.fieldObject.placeholder;
|
|
134
|
+
}
|
|
135
|
+
get validators() {
|
|
136
|
+
return this.fieldObject.validators;
|
|
137
|
+
}
|
|
138
|
+
get choices() {
|
|
139
|
+
return this.fieldObject.choices ?? [];
|
|
140
|
+
}
|
|
141
|
+
set choices(choices) {
|
|
142
|
+
this.fieldObject.choices = choices;
|
|
143
|
+
}
|
|
144
|
+
get visible() {
|
|
145
|
+
return this.fieldObject.visible ?? true;
|
|
146
|
+
}
|
|
147
|
+
get visibleIf() {
|
|
148
|
+
return this.fieldObject.visibleIf;
|
|
149
|
+
}
|
|
150
|
+
get isRequired() {
|
|
151
|
+
return this.fieldObject.required ?? this.fieldObject.isRequired ?? true;
|
|
152
|
+
}
|
|
153
|
+
get startWithNewLine() {
|
|
154
|
+
return this.fieldObject.startWithNewLine ?? true;
|
|
155
|
+
}
|
|
156
|
+
get requiredForEvaluation() {
|
|
157
|
+
return this.fieldObject.requiredForEvaluation ?? true;
|
|
158
|
+
}
|
|
159
|
+
get maxLength() {
|
|
160
|
+
return typeof this.fieldObject.maxLength === 'string'
|
|
161
|
+
? Number.parseInt(this.fieldObject.maxLength)
|
|
162
|
+
: this.fieldObject.maxLength;
|
|
163
|
+
}
|
|
164
|
+
get min() {
|
|
165
|
+
return typeof this.fieldObject.min === 'string'
|
|
166
|
+
? Number.parseFloat(this.fieldObject.min)
|
|
167
|
+
: this.fieldObject.min;
|
|
168
|
+
}
|
|
169
|
+
get max() {
|
|
170
|
+
return typeof this.fieldObject.max === 'string'
|
|
171
|
+
? Number.parseFloat(this.fieldObject.max)
|
|
172
|
+
: this.fieldObject.max;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
export class BasicFormField extends FormField {
|
|
176
|
+
constructor(name, displayName, type, category) {
|
|
177
|
+
super(name, displayName, type, category);
|
|
178
|
+
}
|
|
179
|
+
getFieldNames() {
|
|
180
|
+
if (this.fieldObject.requiredForEvaluation === false) {
|
|
181
|
+
return [];
|
|
182
|
+
}
|
|
183
|
+
return [this.fieldObject.name];
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
export class FileFormField extends FormField {
|
|
187
|
+
constructor(name, displayName, type, category, ihs_column_names) {
|
|
188
|
+
super(name, displayName, type, category);
|
|
189
|
+
this.fieldObject.ihs_column_names = ihs_column_names;
|
|
190
|
+
}
|
|
191
|
+
getFieldNames() {
|
|
192
|
+
if (this.fieldObject.requiredForEvaluation === false) {
|
|
193
|
+
return [];
|
|
194
|
+
}
|
|
195
|
+
return this.fieldObject.ihs_column_names || [];
|
|
196
|
+
}
|
|
197
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,482 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import FormField, { BasicFormField, FileFormField, FieldType, } from "./form-field.js";
|
|
3
|
+
const basicField = {
|
|
4
|
+
name: "email",
|
|
5
|
+
displayName: "Test",
|
|
6
|
+
type: "text",
|
|
7
|
+
inputType: "text",
|
|
8
|
+
category: "1",
|
|
9
|
+
};
|
|
10
|
+
const fileField = {
|
|
11
|
+
name: "test",
|
|
12
|
+
displayName: "Test",
|
|
13
|
+
type: "file",
|
|
14
|
+
inputType: "text",
|
|
15
|
+
category: "1",
|
|
16
|
+
ihs_column_names: ["idNumber", "companyName"],
|
|
17
|
+
};
|
|
18
|
+
describe("FormField", () => {
|
|
19
|
+
it("creates basic field from object", () => {
|
|
20
|
+
const formField = FormField.fromObject(basicField);
|
|
21
|
+
const names = formField.getFieldNames();
|
|
22
|
+
expect(names[0]).toBe("email");
|
|
23
|
+
});
|
|
24
|
+
it("creates file field from object", () => {
|
|
25
|
+
const formField = FormField.fromObject(fileField);
|
|
26
|
+
const names = formField.getFieldNames();
|
|
27
|
+
expect(names[0]).toBe("idNumber");
|
|
28
|
+
expect(names[1]).toBe("companyName");
|
|
29
|
+
});
|
|
30
|
+
it("gets displayName", () => {
|
|
31
|
+
const formField = FormField.fromObject(basicField);
|
|
32
|
+
expect(formField.displayName).toBe("Test");
|
|
33
|
+
});
|
|
34
|
+
it("gets name", () => {
|
|
35
|
+
const formField = FormField.fromObject(basicField);
|
|
36
|
+
expect(formField.name).toBe("email");
|
|
37
|
+
});
|
|
38
|
+
it("sets displayName", () => {
|
|
39
|
+
const formField = FormField.fromObject({ ...basicField });
|
|
40
|
+
formField.displayName = "New Display Name";
|
|
41
|
+
expect(formField.displayName).toBe("New Display Name");
|
|
42
|
+
});
|
|
43
|
+
it("gets visible with true value", () => {
|
|
44
|
+
const fieldObject = {
|
|
45
|
+
name: "test",
|
|
46
|
+
displayName: "Test",
|
|
47
|
+
type: "text",
|
|
48
|
+
category: "1",
|
|
49
|
+
visible: true,
|
|
50
|
+
};
|
|
51
|
+
const formField = FormField.fromObject(fieldObject);
|
|
52
|
+
expect(formField.visible).toBe(true);
|
|
53
|
+
});
|
|
54
|
+
it("gets visible with undefined value defaults to true", () => {
|
|
55
|
+
const fieldObject = {
|
|
56
|
+
name: "test",
|
|
57
|
+
displayName: "Test",
|
|
58
|
+
type: "text",
|
|
59
|
+
category: "1",
|
|
60
|
+
};
|
|
61
|
+
const formField = FormField.fromObject(fieldObject);
|
|
62
|
+
expect(formField.visible).toBe(true);
|
|
63
|
+
});
|
|
64
|
+
it("gets visibleIf", () => {
|
|
65
|
+
const fieldObject = {
|
|
66
|
+
name: "test",
|
|
67
|
+
displayName: "Test",
|
|
68
|
+
type: "text",
|
|
69
|
+
category: "1",
|
|
70
|
+
visibleIf: '{test} == "test"',
|
|
71
|
+
};
|
|
72
|
+
const formField = FormField.fromObject(fieldObject);
|
|
73
|
+
expect(formField.visibleIf).toBe('{test} == "test"');
|
|
74
|
+
});
|
|
75
|
+
it("gets isRequired", () => {
|
|
76
|
+
const fieldObject = {
|
|
77
|
+
name: "test",
|
|
78
|
+
displayName: "Test",
|
|
79
|
+
type: "text",
|
|
80
|
+
category: "1",
|
|
81
|
+
isRequired: true,
|
|
82
|
+
};
|
|
83
|
+
const formField = FormField.fromObject(fieldObject);
|
|
84
|
+
expect(formField.isRequired).toBe(true);
|
|
85
|
+
});
|
|
86
|
+
it("gets type", () => {
|
|
87
|
+
const formField = FormField.fromObject(basicField);
|
|
88
|
+
expect(formField.type).toBe("text");
|
|
89
|
+
});
|
|
90
|
+
it("gets type for various field types", () => {
|
|
91
|
+
const types = ["text", "file", "dropdown"];
|
|
92
|
+
for (const type of types) {
|
|
93
|
+
const fieldObject = {
|
|
94
|
+
name: "test",
|
|
95
|
+
displayName: "Test Field",
|
|
96
|
+
type: type,
|
|
97
|
+
category: "1",
|
|
98
|
+
...(type === "dropdown"
|
|
99
|
+
? { choices: [{ text: "Option", value: "1" }] }
|
|
100
|
+
: {}),
|
|
101
|
+
};
|
|
102
|
+
const formField = FormField.fromObject(fieldObject);
|
|
103
|
+
expect(formField.type).toBe(type);
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
it("gets inputType with defined inputType", () => {
|
|
107
|
+
const fieldObject = {
|
|
108
|
+
name: "test",
|
|
109
|
+
displayName: "Test Field",
|
|
110
|
+
type: "text",
|
|
111
|
+
inputType: "email",
|
|
112
|
+
category: "1",
|
|
113
|
+
};
|
|
114
|
+
const formField = FormField.fromObject(fieldObject);
|
|
115
|
+
expect(formField.inputType).toBe("email");
|
|
116
|
+
});
|
|
117
|
+
it("gets inputType with undefined inputType returns empty string", () => {
|
|
118
|
+
const fieldObject = {
|
|
119
|
+
name: "test",
|
|
120
|
+
displayName: "Test Field",
|
|
121
|
+
type: "text",
|
|
122
|
+
category: "1",
|
|
123
|
+
};
|
|
124
|
+
const formField = FormField.fromObject(fieldObject);
|
|
125
|
+
expect(formField.inputType).toBe("");
|
|
126
|
+
});
|
|
127
|
+
it("gets and sets categoryId", () => {
|
|
128
|
+
const fieldObject = {
|
|
129
|
+
name: "test",
|
|
130
|
+
displayName: "Test Field",
|
|
131
|
+
type: "text",
|
|
132
|
+
category: "5",
|
|
133
|
+
};
|
|
134
|
+
const formField = FormField.fromObject(fieldObject);
|
|
135
|
+
expect(formField.categoryId).toBe("5");
|
|
136
|
+
formField.categoryId = "3";
|
|
137
|
+
expect(formField.categoryId).toBe("3");
|
|
138
|
+
});
|
|
139
|
+
it("gets and sets choices", () => {
|
|
140
|
+
const fieldObject = {
|
|
141
|
+
name: "test",
|
|
142
|
+
displayName: "Test",
|
|
143
|
+
type: "dropdown",
|
|
144
|
+
category: "1",
|
|
145
|
+
choices: [
|
|
146
|
+
{ text: "Option 1", value: "1" },
|
|
147
|
+
{ text: "Option 2", value: "2" },
|
|
148
|
+
],
|
|
149
|
+
};
|
|
150
|
+
const formField = FormField.fromObject(fieldObject);
|
|
151
|
+
expect(formField.choices).toEqual([
|
|
152
|
+
{ text: "Option 1", value: "1" },
|
|
153
|
+
{ text: "Option 2", value: "2" },
|
|
154
|
+
]);
|
|
155
|
+
formField.choices = [{ text: "New", value: "n" }];
|
|
156
|
+
expect(formField.choices).toEqual([{ text: "New", value: "n" }]);
|
|
157
|
+
});
|
|
158
|
+
it("gets choices with undefined choices returns empty array", () => {
|
|
159
|
+
const fieldObject = {
|
|
160
|
+
name: "test",
|
|
161
|
+
displayName: "Test",
|
|
162
|
+
type: "text",
|
|
163
|
+
category: "1",
|
|
164
|
+
};
|
|
165
|
+
const formField = FormField.fromObject(fieldObject);
|
|
166
|
+
expect(formField.choices).toEqual([]);
|
|
167
|
+
});
|
|
168
|
+
it("gets startWithNewLine default true", () => {
|
|
169
|
+
const formField = FormField.fromObject({
|
|
170
|
+
name: "test",
|
|
171
|
+
displayName: "Test",
|
|
172
|
+
type: "text",
|
|
173
|
+
category: "1",
|
|
174
|
+
});
|
|
175
|
+
expect(formField.startWithNewLine).toBe(true);
|
|
176
|
+
});
|
|
177
|
+
it("gets startWithNewLine false", () => {
|
|
178
|
+
const formField = FormField.fromObject({
|
|
179
|
+
name: "test",
|
|
180
|
+
displayName: "Test",
|
|
181
|
+
type: "text",
|
|
182
|
+
category: "1",
|
|
183
|
+
startWithNewLine: false,
|
|
184
|
+
});
|
|
185
|
+
expect(formField.startWithNewLine).toBe(false);
|
|
186
|
+
});
|
|
187
|
+
it("gets requiredForEvaluation default true", () => {
|
|
188
|
+
const formField = FormField.fromObject({
|
|
189
|
+
name: "test",
|
|
190
|
+
displayName: "Test",
|
|
191
|
+
type: "text",
|
|
192
|
+
category: "1",
|
|
193
|
+
});
|
|
194
|
+
expect(formField.requiredForEvaluation).toBe(true);
|
|
195
|
+
});
|
|
196
|
+
it("gets requiredForEvaluation false", () => {
|
|
197
|
+
const formField = FormField.fromObject({
|
|
198
|
+
name: "test",
|
|
199
|
+
displayName: "Test",
|
|
200
|
+
type: "boolean",
|
|
201
|
+
category: "7",
|
|
202
|
+
requiredForEvaluation: false,
|
|
203
|
+
});
|
|
204
|
+
expect(formField.requiredForEvaluation).toBe(false);
|
|
205
|
+
});
|
|
206
|
+
it("parses maxLength, min, max from strings", () => {
|
|
207
|
+
const field = FormField.fromObject({
|
|
208
|
+
name: "test",
|
|
209
|
+
displayName: "Test",
|
|
210
|
+
type: "text",
|
|
211
|
+
category: "1",
|
|
212
|
+
maxLength: "100",
|
|
213
|
+
min: "10.5",
|
|
214
|
+
max: "50.5",
|
|
215
|
+
});
|
|
216
|
+
expect(field.maxLength).toBe(100);
|
|
217
|
+
expect(field.min).toBe(10.5);
|
|
218
|
+
expect(field.max).toBe(50.5);
|
|
219
|
+
});
|
|
220
|
+
it("gets maxLength as number", () => {
|
|
221
|
+
const field = FormField.fromObject({
|
|
222
|
+
name: "test",
|
|
223
|
+
displayName: "Test",
|
|
224
|
+
type: "text",
|
|
225
|
+
category: "1",
|
|
226
|
+
maxLength: 18,
|
|
227
|
+
});
|
|
228
|
+
expect(field.maxLength).toBe(18);
|
|
229
|
+
});
|
|
230
|
+
it("gets undefined for missing optional properties", () => {
|
|
231
|
+
const field = FormField.fromObject({
|
|
232
|
+
name: "test",
|
|
233
|
+
displayName: "Test",
|
|
234
|
+
type: "text",
|
|
235
|
+
category: "1",
|
|
236
|
+
});
|
|
237
|
+
expect(field.placeholder).toBeUndefined();
|
|
238
|
+
expect(field.validators).toBeUndefined();
|
|
239
|
+
expect(field.maxLength).toBeUndefined();
|
|
240
|
+
expect(field.min).toBeUndefined();
|
|
241
|
+
expect(field.max).toBeUndefined();
|
|
242
|
+
});
|
|
243
|
+
it("gets placeholder and validators", () => {
|
|
244
|
+
const field = FormField.fromObject({
|
|
245
|
+
name: "test",
|
|
246
|
+
displayName: "Test",
|
|
247
|
+
type: "text",
|
|
248
|
+
category: "1",
|
|
249
|
+
placeholder: "Enter something",
|
|
250
|
+
validators: [{ type: "numeric", text: "Too high", maxValue: 50 }],
|
|
251
|
+
});
|
|
252
|
+
expect(field.placeholder).toBe("Enter something");
|
|
253
|
+
expect(field.validators).toEqual([
|
|
254
|
+
{ type: "numeric", text: "Too high", maxValue: 50 },
|
|
255
|
+
]);
|
|
256
|
+
});
|
|
257
|
+
it("gets and sets defaultValue", () => {
|
|
258
|
+
const field = FormField.fromObject({
|
|
259
|
+
name: "test",
|
|
260
|
+
displayName: "Test",
|
|
261
|
+
type: "text",
|
|
262
|
+
category: "1",
|
|
263
|
+
defaultValue: "initial-value",
|
|
264
|
+
});
|
|
265
|
+
expect(field.defaultValue).toBe("initial-value");
|
|
266
|
+
field.defaultValue = "new-value";
|
|
267
|
+
expect(field.defaultValue).toBe("new-value");
|
|
268
|
+
expect(field.toJSON().defaultValue).toBe("new-value");
|
|
269
|
+
});
|
|
270
|
+
it("handles numeric defaultValue", () => {
|
|
271
|
+
const field = FormField.fromObject({
|
|
272
|
+
name: "test",
|
|
273
|
+
displayName: "Test",
|
|
274
|
+
type: "text",
|
|
275
|
+
category: "1",
|
|
276
|
+
defaultValue: 12345,
|
|
277
|
+
});
|
|
278
|
+
expect(field.defaultValue).toBe(12345);
|
|
279
|
+
});
|
|
280
|
+
});
|
|
281
|
+
describe("FormField toJSON", () => {
|
|
282
|
+
it("serializes basic field", () => {
|
|
283
|
+
const formField = FormField.fromObject({
|
|
284
|
+
name: "test",
|
|
285
|
+
displayName: "Test",
|
|
286
|
+
type: "text",
|
|
287
|
+
category: "1",
|
|
288
|
+
inputType: "text",
|
|
289
|
+
});
|
|
290
|
+
expect(formField).toBeInstanceOf(BasicFormField);
|
|
291
|
+
expect(formField.toJSON()).toEqual({
|
|
292
|
+
name: "test",
|
|
293
|
+
displayName: "Test",
|
|
294
|
+
type: "text",
|
|
295
|
+
category: "1",
|
|
296
|
+
inputType: "text",
|
|
297
|
+
});
|
|
298
|
+
});
|
|
299
|
+
it("serializes file field", () => {
|
|
300
|
+
const formField = FormField.fromObject({
|
|
301
|
+
name: "test",
|
|
302
|
+
displayName: "Test",
|
|
303
|
+
type: "file",
|
|
304
|
+
category: "1",
|
|
305
|
+
inputType: "text",
|
|
306
|
+
ihs_column_names: ["col1", "col2"],
|
|
307
|
+
});
|
|
308
|
+
expect(formField).toBeInstanceOf(FileFormField);
|
|
309
|
+
expect(formField.toJSON()).toEqual({
|
|
310
|
+
name: "test",
|
|
311
|
+
displayName: "Test",
|
|
312
|
+
type: "file",
|
|
313
|
+
category: "1",
|
|
314
|
+
inputType: "text",
|
|
315
|
+
ihs_column_names: ["col1", "col2"],
|
|
316
|
+
});
|
|
317
|
+
});
|
|
318
|
+
});
|
|
319
|
+
describe("FormField validation", () => {
|
|
320
|
+
it("throws when name is missing", () => {
|
|
321
|
+
expect(() => FormField.fromObject({
|
|
322
|
+
displayName: "Test",
|
|
323
|
+
type: "text",
|
|
324
|
+
category: "1",
|
|
325
|
+
})).toThrow(/name is required/);
|
|
326
|
+
});
|
|
327
|
+
it("throws when displayName is missing", () => {
|
|
328
|
+
expect(() => FormField.fromObject({
|
|
329
|
+
name: "test",
|
|
330
|
+
type: "text",
|
|
331
|
+
category: "1",
|
|
332
|
+
})).toThrow(/missing displayName or category/);
|
|
333
|
+
});
|
|
334
|
+
it("throws when category is missing", () => {
|
|
335
|
+
expect(() => FormField.fromObject({
|
|
336
|
+
name: "test",
|
|
337
|
+
displayName: "Test",
|
|
338
|
+
type: "text",
|
|
339
|
+
})).toThrow(/missing displayName or category/);
|
|
340
|
+
});
|
|
341
|
+
it("throws when both displayName and category are missing", () => {
|
|
342
|
+
expect(() => FormField.fromObject({
|
|
343
|
+
name: "test",
|
|
344
|
+
type: "text",
|
|
345
|
+
})).toThrow(/missing displayName or category/);
|
|
346
|
+
});
|
|
347
|
+
it("throws when displayName is empty string", () => {
|
|
348
|
+
expect(() => FormField.fromObject({
|
|
349
|
+
name: "test",
|
|
350
|
+
displayName: "",
|
|
351
|
+
type: "text",
|
|
352
|
+
category: "1",
|
|
353
|
+
})).toThrow(/missing displayName or category/);
|
|
354
|
+
});
|
|
355
|
+
it("throws when displayName and category are null", () => {
|
|
356
|
+
expect(() => FormField.fromObject({
|
|
357
|
+
name: "test",
|
|
358
|
+
displayName: null,
|
|
359
|
+
type: "text",
|
|
360
|
+
category: null,
|
|
361
|
+
})).toThrow(/missing displayName or category/);
|
|
362
|
+
});
|
|
363
|
+
it("throws for invalid field type", () => {
|
|
364
|
+
expect(() => FormField.fromObject({
|
|
365
|
+
name: "test",
|
|
366
|
+
displayName: "Test",
|
|
367
|
+
type: "invalid",
|
|
368
|
+
category: "1",
|
|
369
|
+
})).toThrow();
|
|
370
|
+
});
|
|
371
|
+
it("throws for dropdown without choices", () => {
|
|
372
|
+
expect(() => FormField.fromObject({
|
|
373
|
+
name: "test",
|
|
374
|
+
displayName: "Test",
|
|
375
|
+
type: "dropdown",
|
|
376
|
+
category: "1",
|
|
377
|
+
})).toThrow(/type dropdown, but does not have choices/);
|
|
378
|
+
});
|
|
379
|
+
it("allows choices on non-dropdown fields", () => {
|
|
380
|
+
expect(() => FormField.fromObject({
|
|
381
|
+
name: "test",
|
|
382
|
+
displayName: "Test",
|
|
383
|
+
type: "text",
|
|
384
|
+
category: "1",
|
|
385
|
+
choices: [{ text: "testChoice", value: "01" }],
|
|
386
|
+
})).not.toThrow();
|
|
387
|
+
});
|
|
388
|
+
it("allows unknown inputTypes", () => {
|
|
389
|
+
expect(() => FormField.fromObject({
|
|
390
|
+
name: "test",
|
|
391
|
+
displayName: "Test",
|
|
392
|
+
type: "text",
|
|
393
|
+
inputType: "invalid",
|
|
394
|
+
category: "1",
|
|
395
|
+
})).not.toThrow();
|
|
396
|
+
});
|
|
397
|
+
});
|
|
398
|
+
describe("FormField runtime - all field types", () => {
|
|
399
|
+
it("correctly identifies all supported field types", () => {
|
|
400
|
+
const validTypes = [
|
|
401
|
+
{ input: "text", expected: FieldType.TEXT },
|
|
402
|
+
{ input: "file", expected: FieldType.FILE },
|
|
403
|
+
{ input: "dropdown", expected: FieldType.DROPDOWN },
|
|
404
|
+
{ input: "boolean", expected: FieldType.BOOLEAN },
|
|
405
|
+
{ input: "checkbox", expected: FieldType.CHECKBOX },
|
|
406
|
+
{ input: "radiogroup", expected: FieldType.RADIOGROUP },
|
|
407
|
+
{ input: "slider", expected: FieldType.SLIDER },
|
|
408
|
+
{ input: "html", expected: FieldType.HTML },
|
|
409
|
+
{ input: "tagbox", expected: FieldType.TAGBOX },
|
|
410
|
+
];
|
|
411
|
+
for (const { input, expected } of validTypes) {
|
|
412
|
+
const fieldData = {
|
|
413
|
+
name: "test",
|
|
414
|
+
displayName: "Test",
|
|
415
|
+
type: input,
|
|
416
|
+
category: "cat1",
|
|
417
|
+
};
|
|
418
|
+
if (input === "dropdown") {
|
|
419
|
+
fieldData.choices = [{ text: "Yes", value: "yes" }];
|
|
420
|
+
}
|
|
421
|
+
const field = FormField.fromObject(fieldData);
|
|
422
|
+
expect(field.type).toBe(expected);
|
|
423
|
+
}
|
|
424
|
+
});
|
|
425
|
+
it("throws error for invalid or unknown field types", () => {
|
|
426
|
+
const invalidInputs = ["invalid", "", "something_else", null, undefined];
|
|
427
|
+
for (const input of invalidInputs) {
|
|
428
|
+
expect(() => {
|
|
429
|
+
FormField.fromObject({
|
|
430
|
+
name: "test",
|
|
431
|
+
displayName: "Test",
|
|
432
|
+
type: input,
|
|
433
|
+
category: "cat1",
|
|
434
|
+
});
|
|
435
|
+
}).toThrow();
|
|
436
|
+
}
|
|
437
|
+
});
|
|
438
|
+
});
|
|
439
|
+
describe("FileFormField", () => {
|
|
440
|
+
it("returns empty when requiredForEvaluation is false", () => {
|
|
441
|
+
const field = FormField.fromObject({
|
|
442
|
+
name: "file_field",
|
|
443
|
+
displayName: "File Field",
|
|
444
|
+
type: "file",
|
|
445
|
+
category: "cat1",
|
|
446
|
+
requiredForEvaluation: false,
|
|
447
|
+
ihs_column_names: ["col1", "col2"],
|
|
448
|
+
});
|
|
449
|
+
expect(field.getFieldNames()).toEqual([]);
|
|
450
|
+
});
|
|
451
|
+
it("returns ihs_column_names when requiredForEvaluation is true", () => {
|
|
452
|
+
const field = FormField.fromObject({
|
|
453
|
+
name: "file_field",
|
|
454
|
+
displayName: "File Field",
|
|
455
|
+
type: "file",
|
|
456
|
+
category: "cat1",
|
|
457
|
+
ihs_column_names: ["col1", "col2"],
|
|
458
|
+
});
|
|
459
|
+
expect(field.getFieldNames()).toEqual(["col1", "col2"]);
|
|
460
|
+
});
|
|
461
|
+
});
|
|
462
|
+
describe("BasicFormField", () => {
|
|
463
|
+
it("returns empty when requiredForEvaluation is false", () => {
|
|
464
|
+
const field = FormField.fromObject({
|
|
465
|
+
name: "test",
|
|
466
|
+
displayName: "Test",
|
|
467
|
+
type: "text",
|
|
468
|
+
category: "1",
|
|
469
|
+
requiredForEvaluation: false,
|
|
470
|
+
});
|
|
471
|
+
expect(field.getFieldNames()).toEqual([]);
|
|
472
|
+
});
|
|
473
|
+
it("returns field name when requiredForEvaluation is true", () => {
|
|
474
|
+
const field = FormField.fromObject({
|
|
475
|
+
name: "test",
|
|
476
|
+
displayName: "Test",
|
|
477
|
+
type: "text",
|
|
478
|
+
category: "1",
|
|
479
|
+
});
|
|
480
|
+
expect(field.getFieldNames()).toEqual(["test"]);
|
|
481
|
+
});
|
|
482
|
+
});
|