@planning-inspectorate/dynamic-forms 3.13.0 → 3.14.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@planning-inspectorate/dynamic-forms",
3
- "version": "3.13.0",
3
+ "version": "3.14.0",
4
4
  "description": "This package is for building GDS forms using a configuration-based approach.",
5
5
  "main": "./src/index.js",
6
6
  "type": "module",
@@ -1,7 +1,5 @@
1
1
  import escape from 'escape-html';
2
- import RequiredValidator from '../validator/required-validator.js';
3
2
  import { capitalize, nl2br, trimTrailingSlash } from '../lib/utils.js';
4
- import AddressValidator from '../validator/address-validator.js';
5
3
  import MultiFieldInputValidator from '../validator/multi-field-input-validator.js';
6
4
  import { answerObjectForManageList } from '#src/components/manage-list/utils.js';
7
5
 
@@ -454,12 +452,7 @@ export class Question {
454
452
  * @returns {boolean}
455
453
  */
456
454
  isRequired() {
457
- return this.validators?.some(
458
- (item) =>
459
- item instanceof RequiredValidator ||
460
- (item instanceof AddressValidator && item.isRequired()) ||
461
- (item instanceof MultiFieldInputValidator && item.isRequired())
462
- );
455
+ return this.validators?.some((validator) => validator.isRequired());
463
456
  }
464
457
  /**
465
458
  * @param {string} inputField
@@ -437,4 +437,17 @@ describe('./src/dynamic-forms/question.js', () => {
437
437
  });
438
438
  });
439
439
  });
440
+
441
+ describe('isRequired', () => {
442
+ it('should not be required if no validators', () => {
443
+ const q = getTestQuestion({ validators: [] });
444
+ assert.strictEqual(q.isRequired(), false);
445
+ });
446
+ it('should be required if any validator is required', () => {
447
+ const q = getTestQuestion({
448
+ validators: [{ isRequired: () => false }, { isRequired: () => true }]
449
+ });
450
+ assert.strictEqual(q.isRequired(), true);
451
+ });
452
+ });
440
453
  });
@@ -26,6 +26,16 @@ export class BaseValidator {
26
26
  validate(questionObj, journeyResponse) {
27
27
  throw new Error('validate method must be implemented by subclass');
28
28
  }
29
+
30
+ /**
31
+ * Should the question that this validator is configured with be treated as a required question?
32
+ *
33
+ * Validators should override this method as appropriate, implementing any custom required logic.
34
+ * For example, the AddressValidator is configurable and is required if any one field is required.
35
+ */
36
+ isRequired() {
37
+ return false;
38
+ }
29
39
  }
30
40
 
31
41
  export default BaseValidator;
@@ -1,5 +1,4 @@
1
1
  import { body } from 'express-validator';
2
-
3
2
  import { isAfter, isBefore, isValid, parse } from 'date-fns';
4
3
  import { enGB } from 'date-fns/locale';
5
4
 
@@ -12,8 +11,9 @@ import { endOfDay, parseDateInput, startOfDay } from '../lib/date-utils.js';
12
11
 
13
12
  /**
14
13
  * @typedef {Object} DateValidationSettings
15
- * @property {Boolean} ensureFuture
16
- * @property {Boolean} ensurePast
14
+ * @property {Boolean} [ensureFuture]
15
+ * @property {Boolean} [ensurePast]
16
+ * @property {Boolean} [optional]
17
17
  */
18
18
 
19
19
  /**
@@ -34,7 +34,8 @@ export class DateValidator extends BaseValidator {
34
34
  inputLabel,
35
35
  dateValidationSettings = {
36
36
  ensureFuture: false,
37
- ensurePast: false
37
+ ensurePast: false,
38
+ optional: false
38
39
  },
39
40
  errorMessages
40
41
  ) {
@@ -62,6 +63,10 @@ export class DateValidator extends BaseValidator {
62
63
  this.dateValidationSettings = dateValidationSettings;
63
64
  }
64
65
 
66
+ isRequired() {
67
+ return !this.dateValidationSettings.optional;
68
+ }
69
+
65
70
  /**
66
71
  * validates the response body, checking the values sent for the date are valid
67
72
  * @param {DateQuestion} questionObj
@@ -72,9 +77,21 @@ export class DateValidator extends BaseValidator {
72
77
  const monthInput = `${fieldName}_month`;
73
78
  const yearInput = `${fieldName}_year`;
74
79
 
80
+ /**
81
+ * Run before every check, if the correct param set then skip the validation
82
+ * when there is no data entered.
83
+ */
84
+ const shouldValidate = (value, { req }) => {
85
+ if (this.dateValidationSettings.optional === true) {
86
+ return !!(req.body[dayInput] || req.body[monthInput] || req.body[yearInput]);
87
+ }
88
+ return true;
89
+ };
90
+
75
91
  const rules = [
76
92
  // check all or some date inputs are not empty
77
93
  body(dayInput)
94
+ .if(shouldValidate)
78
95
  .notEmpty()
79
96
  .withMessage((_, { req }) => {
80
97
  if (!req.body[monthInput] && !req.body[yearInput]) {
@@ -92,6 +109,7 @@ export class DateValidator extends BaseValidator {
92
109
  return this.noDayErrorMessage;
93
110
  }),
94
111
  body(monthInput)
112
+ .if(shouldValidate)
95
113
  .notEmpty()
96
114
  .withMessage((_, { req }) => {
97
115
  if (req.body[dayInput] && !req.body[yearInput]) {
@@ -104,6 +122,7 @@ export class DateValidator extends BaseValidator {
104
122
  // empty error message returned
105
123
  }),
106
124
  body(yearInput)
125
+ .if(shouldValidate)
107
126
  .notEmpty()
108
127
  .withMessage((_, { req }) => {
109
128
  if (req.body[dayInput] && req.body[monthInput]) return this.noYearErrorMessage;
@@ -113,6 +132,7 @@ export class DateValidator extends BaseValidator {
113
132
 
114
133
  // check date values entered are within valid ranges
115
134
  body(dayInput)
135
+ .if(shouldValidate)
116
136
  .isInt({ min: 1, max: 31 })
117
137
  .withMessage(this.invalidDateErrorMessage)
118
138
  .bail()
@@ -131,38 +151,42 @@ export class DateValidator extends BaseValidator {
131
151
 
132
152
  return true;
133
153
  }),
134
- body(monthInput).isInt({ min: 1, max: 12 }).withMessage(this.invalidMonthErrorMessage),
135
- body(yearInput).isInt({ min: 1000, max: 9999 }).withMessage(this.invalidYearErrorMessage)
154
+ body(monthInput).if(shouldValidate).isInt({ min: 1, max: 12 }).withMessage(this.invalidMonthErrorMessage),
155
+ body(yearInput).if(shouldValidate).isInt({ min: 1000, max: 9999 }).withMessage(this.invalidYearErrorMessage)
136
156
  ];
137
157
 
138
158
  if (this.dateValidationSettings.ensurePast === true) {
139
159
  rules.push(
140
- body(dayInput).custom((value, { req }) => {
141
- const inputDate = parseDateInput({ day: value, month: req.body[monthInput], year: req.body[yearInput] });
160
+ body(dayInput)
161
+ .if(shouldValidate)
162
+ .custom((value, { req }) => {
163
+ const inputDate = parseDateInput({ day: value, month: req.body[monthInput], year: req.body[yearInput] });
142
164
 
143
- const today = endOfDay();
165
+ const today = endOfDay();
144
166
 
145
- if (isAfter(inputDate, today)) {
146
- throw new Error(this.futureDateErrorMessage);
147
- }
167
+ if (isAfter(inputDate, today)) {
168
+ throw new Error(this.futureDateErrorMessage);
169
+ }
148
170
 
149
- return true;
150
- })
171
+ return true;
172
+ })
151
173
  );
152
174
  }
153
175
 
154
176
  if (this.dateValidationSettings.ensureFuture === true) {
155
177
  rules.push(
156
- body(dayInput).custom((value, { req }) => {
157
- const inputDate = parseDateInput({ day: value, month: req.body[monthInput], year: req.body[yearInput] });
158
- const today = startOfDay();
159
-
160
- if (isBefore(inputDate, today)) {
161
- throw new Error(this.pastDateErrorMessage);
162
- }
163
-
164
- return true;
165
- })
178
+ body(dayInput)
179
+ .if(shouldValidate)
180
+ .custom((value, { req }) => {
181
+ const inputDate = parseDateInput({ day: value, month: req.body[monthInput], year: req.body[yearInput] });
182
+ const today = startOfDay();
183
+
184
+ if (isBefore(inputDate, today)) {
185
+ throw new Error(this.pastDateErrorMessage);
186
+ }
187
+
188
+ return true;
189
+ })
166
190
  );
167
191
  }
168
192
 
@@ -389,6 +389,109 @@ describe('./src/dynamic-forms/validator/date-validator.js', () => {
389
389
  assert.strictEqual(errors[`${question.fieldName}_month`].msg, errorMessages.invalidMonthErrorMessage);
390
390
  assert.strictEqual(errors[`${question.fieldName}_year`].msg, errorMessages.invalidYearErrorMessage);
391
391
  });
392
+
393
+ it('passes validation if optional is true and no fields are provided (undefined)', async () => {
394
+ const req = {
395
+ body: {
396
+ ['date-question_day']: undefined,
397
+ ['date-question_month']: undefined,
398
+ ['date-question_year']: undefined
399
+ }
400
+ };
401
+
402
+ const question = {
403
+ fieldName: 'date-question'
404
+ };
405
+
406
+ const errors = await _validationMappedErrors(req, question, 'the required date', undefined, {
407
+ optional: true
408
+ });
409
+
410
+ assert.strictEqual(Object.keys(errors).length, 0);
411
+ });
412
+
413
+ it('passes validation if optional is true and fields are empty strings', async () => {
414
+ const req = {
415
+ body: {
416
+ ['date-question_day']: '',
417
+ ['date-question_month']: '',
418
+ ['date-question_year']: ''
419
+ }
420
+ };
421
+
422
+ const question = {
423
+ fieldName: 'date-question'
424
+ };
425
+
426
+ const errors = await _validationMappedErrors(req, question, 'the required date', undefined, {
427
+ optional: true
428
+ });
429
+
430
+ assert.strictEqual(Object.keys(errors).length, 0);
431
+ });
432
+
433
+ it('throws error if optional is true but date is partially filled (missing day)', async () => {
434
+ const req = {
435
+ body: {
436
+ ['date-question_day']: '',
437
+ ['date-question_month']: '10',
438
+ ['date-question_year']: '2023'
439
+ }
440
+ };
441
+
442
+ const question = {
443
+ fieldName: 'date-question'
444
+ };
445
+
446
+ const errors = await _validationMappedErrors(req, question, 'the required date', undefined, {
447
+ optional: true
448
+ });
449
+
450
+ assert.strictEqual(Object.keys(errors).length, 1);
451
+ assert.strictEqual(errors[`${question.fieldName}_day`].msg, 'The required date must include a day');
452
+ });
453
+
454
+ it('throws error if optional is true but invalid values are provided', async () => {
455
+ const req = {
456
+ body: {
457
+ ['date-question_day']: '35',
458
+ ['date-question_month']: '10',
459
+ ['date-question_year']: '2023'
460
+ }
461
+ };
462
+
463
+ const question = {
464
+ fieldName: 'date-question'
465
+ };
466
+
467
+ const errors = await _validationMappedErrors(req, question, 'the required date', undefined, {
468
+ optional: true
469
+ });
470
+
471
+ assert.strictEqual(Object.keys(errors).length, 1);
472
+ assert.strictEqual(errors[`${question.fieldName}_day`].msg, 'The required date day must be a real day');
473
+ });
474
+
475
+ it('throws error if an object is passed without the optional key (undefined) and nothing is entered', async () => {
476
+ const req = {
477
+ body: {
478
+ ['date-question_day']: undefined,
479
+ ['date-question_month']: undefined,
480
+ ['date-question_year']: undefined
481
+ }
482
+ };
483
+
484
+ const question = {
485
+ fieldName: 'date-question'
486
+ };
487
+
488
+ const errors = await _validationMappedErrors(req, question, 'the required date', undefined, {
489
+ randomKey: true
490
+ });
491
+
492
+ assert.strictEqual(Object.keys(errors).length, 3);
493
+ assert.strictEqual(errors[`${question.fieldName}_day`].msg, 'Enter the required date');
494
+ });
392
495
  });
393
496
  });
394
497
 
@@ -35,6 +35,10 @@ export class RequiredValidator extends BaseValidator {
35
35
  validate(questionObj) {
36
36
  return body(questionObj.fieldName).notEmpty().withMessage(this.errorMessage);
37
37
  }
38
+
39
+ isRequired() {
40
+ return true;
41
+ }
38
42
  }
39
43
 
40
44
  export default RequiredValidator;
@@ -0,0 +1,130 @@
1
+ import { describe, it } from 'node:test';
2
+ import { COMPONENT_TYPES } from '#src/components/utils/component-types.js';
3
+ import { createQuestions } from '#src/questions/create-questions.js';
4
+ import { questionClasses } from '#src/questions/questions.js';
5
+ import { Section, SECTION_STATUS } from '#section';
6
+ import assert from 'node:assert';
7
+ import { JourneyResponse } from '#journey-response';
8
+ import RequiredValidator from '#src/validator/required-validator.js';
9
+ import DateValidator from '#src/validator/date-validator.js';
10
+ import AddressValidator from '#src/validator/address-validator.js';
11
+
12
+ /**
13
+ * @typedef {import('../src/questions/question-props.js').QuestionProps} Props
14
+ */
15
+
16
+ /**
17
+ * Check logic related to the question.isRequired method, which in turn drives the section status logic
18
+ */
19
+ describe('is-required', () => {
20
+ /** @type {() => Record<string, Props>} */
21
+ const getQuestionProps = () => ({
22
+ holidayActivities: {
23
+ type: COMPONENT_TYPES.CHECKBOX,
24
+ title: 'Holiday Activities',
25
+ question: 'Which activities do you want to do on holiday?',
26
+ fieldName: 'holidayActivities',
27
+ url: 'holiday-activities',
28
+ label: 'Select all that apply',
29
+ options: [
30
+ { value: 'swimming', text: 'Swimming' },
31
+ { value: 'hiking', text: 'Hiking' },
32
+ { value: 'sightseeing', text: 'Sightseeing' }
33
+ ]
34
+ },
35
+ addInsurance: {
36
+ type: COMPONENT_TYPES.BOOLEAN,
37
+ title: 'Travel Insurance',
38
+ question: 'Do you want to add travel insurance to your holiday?',
39
+ fieldName: 'addInsurance',
40
+ url: 'add-insurance',
41
+ label: 'Add insurance?'
42
+ },
43
+ travelInsuranceType: {
44
+ type: COMPONENT_TYPES.RADIO,
45
+ title: 'Travel Insurance Type',
46
+ question: 'Which type of travel insurance would you like?',
47
+ fieldName: 'travelInsuranceType',
48
+ url: 'travel-insurance-type',
49
+ label: 'Select an insurance type',
50
+ options: [
51
+ { value: 'basic', text: 'Basic' },
52
+ { value: 'comprehensive', text: 'Comprehensive' },
53
+ { value: 'premium', text: 'Premium' }
54
+ ]
55
+ },
56
+ holidayArrival: {
57
+ type: COMPONENT_TYPES.DATE_TIME,
58
+ title: 'Holiday Arrival',
59
+ question: 'When would you like to arrive at your destination?',
60
+ fieldName: 'holidayArrival',
61
+ url: 'holiday-arrival',
62
+ label: 'Enter your arrival time'
63
+ },
64
+ hotelAddress: {
65
+ type: COMPONENT_TYPES.ADDRESS,
66
+ title: 'Hotel Address',
67
+ question: 'What is the address of your hotel?',
68
+ fieldName: 'hotelAddress',
69
+ url: 'hotel-address',
70
+ label: 'Hotel address'
71
+ }
72
+ });
73
+
74
+ const newSection = (questionProps) => {
75
+ const questions = createQuestions(questionProps, questionClasses, {});
76
+
77
+ return new Section('test', 'test')
78
+ .addQuestion(questions.holidayActivities)
79
+ .addQuestion(questions.addInsurance)
80
+ .addQuestion(questions.travelInsuranceType)
81
+ .addQuestion(questions.holidayArrival)
82
+ .addQuestion(questions.hotelAddress);
83
+ };
84
+ const sectionStatus = (questionProps, answers = {}) => {
85
+ const section = newSection(questionProps);
86
+
87
+ const jr = new JourneyResponse('id-1', '', answers);
88
+ return section.getStatus(jr);
89
+ };
90
+
91
+ it('should be section=complete if no questions are required', () => {
92
+ const props = getQuestionProps();
93
+ assert.strictEqual(sectionStatus(props), SECTION_STATUS.COMPLETE);
94
+ });
95
+ it('should be section=not started if any question is required', () => {
96
+ const props = getQuestionProps();
97
+
98
+ // for each question, mark each one required one at a time
99
+ for (const key of Object.keys(props)) {
100
+ const copy = getQuestionProps();
101
+ copy[key].validators = [new RequiredValidator()];
102
+ assert.strictEqual(sectionStatus(copy), SECTION_STATUS.NOT_STARTED);
103
+ }
104
+ });
105
+ it('should treat date validator as required', () => {
106
+ const props = getQuestionProps();
107
+ props.holidayArrival.validators = [new DateValidator('My Date')];
108
+ assert.strictEqual(sectionStatus(props, {}), SECTION_STATUS.NOT_STARTED);
109
+ });
110
+ it('should treat address validator as required if there are required fields', () => {
111
+ const props = getQuestionProps();
112
+ props.hotelAddress.validators = [
113
+ new AddressValidator({
114
+ requiredFields: {
115
+ addressLine1: true,
116
+ addressLine2: false,
117
+ county: false,
118
+ townCity: false,
119
+ postcode: true
120
+ }
121
+ })
122
+ ];
123
+ assert.strictEqual(sectionStatus(props, {}), SECTION_STATUS.NOT_STARTED);
124
+ });
125
+ it('should not treat address validator as required if there are no required fields', () => {
126
+ const props = getQuestionProps();
127
+ props.hotelAddress.validators = [new AddressValidator()];
128
+ assert.strictEqual(sectionStatus(props, {}), SECTION_STATUS.COMPLETE);
129
+ });
130
+ });
@@ -1 +1 @@
1
- {"version":3,"file":"question.d.ts","sourceRoot":"","sources":["../../../src/questions/question.js"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"question.d.ts","sourceRoot":"","sources":["../../../src/questions/question.js"],"names":[],"mappings":"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"}
@@ -40,7 +40,6 @@ export class AddressValidator extends BaseValidator {
40
40
  * @param {Question} questionObj
41
41
  */
42
42
  validate(questionObj: Question): import("express-validator").ValidationChain[];
43
- isRequired(): boolean;
44
43
  #private;
45
44
  }
46
45
  export default AddressValidator;
@@ -1 +1 @@
1
- {"version":3,"file":"address-validator.d.ts","sourceRoot":"","sources":["../../../src/validator/address-validator.js"],"names":[],"mappings":"AAIO,4EAQN;AAGD,oCAAqC,GAAG,CAAC;AACzC,oCAAqC,CAAC,CAAC;AACvC,oCAAqC,GAAG,CAAC;AACzC,oCAAqC,CAAC,CAAC;AACvC,gCAAiC,GAAG,CAAC;AACrC,gCAAiC,CAAC,CAAC;AACnC,8BAA+B,GAAG,CAAC;AACnC,8BAA+B,CAAC,CAAC;AACjC,gCAAiC,CAAC,CAAC;AACnC,gCAAiC,CAAC,CAAC;AAEnC;;;GAGG;AACH;IACC;;;;OAIG;IACH,mBAFG;QAAqH,cAAc;0BAA5G,OAAO;0BAAgB,OAAO;sBAAY,OAAO;oBAAU,OAAO;sBAAY,OAAO;;KAAwB,EAKtI;IADA;sBAJyB,OAAO;sBAAgB,OAAO;kBAAY,OAAO;gBAAU,OAAO;kBAAY,OAAO;kBAIpE;IAG3C;;;OAGG;IACH,sBAFW,QAAQ,iDAYlB;IA+FD,sBAKC;;CACD;;0BA1JyB,qBAAqB"}
1
+ {"version":3,"file":"address-validator.d.ts","sourceRoot":"","sources":["../../../src/validator/address-validator.js"],"names":[],"mappings":"AAIO,4EAQN;AAGD,oCAAqC,GAAG,CAAC;AACzC,oCAAqC,CAAC,CAAC;AACvC,oCAAqC,GAAG,CAAC;AACzC,oCAAqC,CAAC,CAAC;AACvC,gCAAiC,GAAG,CAAC;AACrC,gCAAiC,CAAC,CAAC;AACnC,8BAA+B,GAAG,CAAC;AACnC,8BAA+B,CAAC,CAAC;AACjC,gCAAiC,CAAC,CAAC;AACnC,gCAAiC,CAAC,CAAC;AAEnC;;;GAGG;AACH;IACC;;;;OAIG;IACH,mBAFG;QAAqH,cAAc;0BAA5G,OAAO;0BAAgB,OAAO;sBAAY,OAAO;oBAAU,OAAO;sBAAY,OAAO;;KAAwB,EAKtI;IADA;sBAJyB,OAAO;sBAAgB,OAAO;kBAAY,OAAO;gBAAU,OAAO;kBAAY,OAAO;kBAIpE;IAG3C;;;OAGG;IACH,sBAFW,QAAQ,iDAYlB;;CAqGD;;0BA1JyB,qBAAqB"}
@@ -16,6 +16,13 @@ export class BaseValidator {
16
16
  * @returns {import('express-validator').ValidationChain | import('express-validator').ValidationChain[]}
17
17
  */
18
18
  validate(questionObj: import("../questions/question-props.js").QuestionProps, journeyResponse?: import("../journey/journey-response.js").JourneyResponse): import("express-validator").ValidationChain | import("express-validator").ValidationChain[];
19
+ /**
20
+ * Should the question that this validator is configured with be treated as a required question?
21
+ *
22
+ * Validators should override this method as appropriate, implementing any custom required logic.
23
+ * For example, the AddressValidator is configurable and is required if any one field is required.
24
+ */
25
+ isRequired(): boolean;
19
26
  }
20
27
  export default BaseValidator;
21
28
  //# sourceMappingURL=base-validator.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"base-validator.d.ts","sourceRoot":"","sources":["../../../src/validator/base-validator.js"],"names":[],"mappings":"AAAA;;;GAGG;AACH;IACC;;OAEG;IACH,cAFU,MAAM,CAEH;IAQb;;;;;;;OAOG;IAEH,sBALW,OAAO,gCAAgC,EAAE,aAAa,oBACtD,OAAO,gCAAgC,EAAE,eAAe,GACtD,OAAO,mBAAmB,EAAE,eAAe,GAAG,OAAO,mBAAmB,EAAE,eAAe,EAAE,CAKvG;CACD"}
1
+ {"version":3,"file":"base-validator.d.ts","sourceRoot":"","sources":["../../../src/validator/base-validator.js"],"names":[],"mappings":"AAAA;;;GAGG;AACH;IACC;;OAEG;IACH,cAFU,MAAM,CAEH;IAQb;;;;;;;OAOG;IAEH,sBALW,OAAO,gCAAgC,EAAE,aAAa,oBACtD,OAAO,gCAAgC,EAAE,eAAe,GACtD,OAAO,mBAAmB,EAAE,eAAe,GAAG,OAAO,mBAAmB,EAAE,eAAe,EAAE,CAKvG;IAED;;;;;OAKG;IACH,sBAEC;CACD"}
@@ -3,8 +3,9 @@
3
3
  */
4
4
  /**
5
5
  * @typedef {Object} DateValidationSettings
6
- * @property {Boolean} ensureFuture
7
- * @property {Boolean} ensurePast
6
+ * @property {Boolean} [ensureFuture]
7
+ * @property {Boolean} [ensurePast]
8
+ * @property {Boolean} [optional]
8
9
  */
9
10
  /**
10
11
  * enforces a user has entered a valid date
@@ -42,8 +43,9 @@ export class DateValidator extends BaseValidator {
42
43
  export default DateValidator;
43
44
  export type DateQuestion = typeof import("../components/date/question.js");
44
45
  export type DateValidationSettings = {
45
- ensureFuture: boolean;
46
- ensurePast: boolean;
46
+ ensureFuture?: boolean | undefined;
47
+ ensurePast?: boolean | undefined;
48
+ optional?: boolean | undefined;
47
49
  };
48
50
  import BaseValidator from './base-validator.js';
49
51
  //# sourceMappingURL=date-validator.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"date-validator.d.ts","sourceRoot":"","sources":["../../../src/validator/date-validator.js"],"names":[],"mappings":"AAQA;;GAEG;AAEH;;;;GAIG;AAEH;;;GAGG;AACH;IAIC;;;;;OAKG;IACH,wBAJW,MAAM,2BACN,sBAAsB,kBACtB,MAAM,EAgChB;IAvCD,qCAAqC;IACrC,wBADW,sBAAsB,CACV;IAoBtB,uBAAmG;IACnG,uBAAmG;IACnG,yBAAyG;IACzG,wBAAsG;IACtG,4BAAkH;IAClH,2BAA+G;IAC/G,6BACuF;IACvF,6BACuF;IACvF,8BACyF;IACzF,6BACuF;IACvF,4BAAkH;IAClH,0BAA4G;IAK7G;;;OAGG;IACH,sBAFW,YAAY,iDAuGtB;;CA6BD;;;;;;;0BAjMyB,qBAAqB"}
1
+ {"version":3,"file":"date-validator.d.ts","sourceRoot":"","sources":["../../../src/validator/date-validator.js"],"names":[],"mappings":"AAOA;;GAEG;AAEH;;;;;GAKG;AAEH;;;GAGG;AACH;IAIC;;;;;OAKG;IACH,wBAJW,MAAM,2BACN,sBAAsB,kBACtB,MAAM,EAiChB;IAxCD,qCAAqC;IACrC,wBADW,sBAAsB,CACV;IAqBtB,uBAAmG;IACnG,uBAAmG;IACnG,yBAAyG;IACzG,wBAAsG;IACtG,4BAAkH;IAClH,2BAA+G;IAC/G,6BACuF;IACvF,6BACuF;IACvF,8BACyF;IACzF,6BACuF;IACvF,4BAAkH;IAClH,0BAA4G;IAS7G;;;OAGG;IACH,sBAFW,YAAY,iDA0HtB;;CA6BD;;;;;;;;0BA1NyB,qBAAqB"}
@@ -51,7 +51,6 @@ export class MultiFieldInputValidator extends BaseValidator {
51
51
  * validates response body against question's required fields
52
52
  */
53
53
  validate(): import("express-validator").ValidationChain[];
54
- isRequired(): boolean;
55
54
  /**
56
55
  * checks if a field is required
57
56
  * @param {string} fieldName
@@ -1 +1 @@
1
- {"version":3,"file":"multi-field-input-validator.d.ts","sourceRoot":"","sources":["../../../src/validator/multi-field-input-validator.js"],"names":[],"mappings":"AAGA;;;;GAIG;AAEH;;;;GAIG;AAEH;;;;GAIG;AAEH;;;;;;GAMG;AAEH;;;;;GAKG;AAEH;;;;;GAKG;AAEH;;GAEG;AAEH;IACC;;;;OAIG;IACH,0CAHG;QAAyB,MAAM;QACP,eAAe;KAAC,EAQ1C;IAFA,gBAAoB;IACpB,wBAAwE;IAGzE;;OAEG;IAEH,0DA8BC;IAED,sBAEC;IACD;;;;OAIG;IACH,gCAHW,MAAM,GACJ,OAAO,CAQnB;CACD;;;WA3Ga,SAAS,MAAM;;;;;;;;;;;;eAkBf,MAAM;;;;;4BAOP,SAAS,GAAG;IACpB,QAAQ,EAAE,IAAI,CAAC;IACf,YAAY,EAAE,MAAM,CAAA;CACrB;4BAIS,SAAS,GAAG;IACpB,QAAQ,CAAC,EAAE,KAAK,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;oBAIS,aAAa,GAAG,aAAa;0BA1ChB,qBAAqB"}
1
+ {"version":3,"file":"multi-field-input-validator.d.ts","sourceRoot":"","sources":["../../../src/validator/multi-field-input-validator.js"],"names":[],"mappings":"AAGA;;;;GAIG;AAEH;;;;GAIG;AAEH;;;;GAIG;AAEH;;;;;;GAMG;AAEH;;;;;GAKG;AAEH;;;;;GAKG;AAEH;;GAEG;AAEH;IACC;;;;OAIG;IACH,0CAHG;QAAyB,MAAM;QACP,eAAe;KAAC,EAQ1C;IAFA,gBAAoB;IACpB,wBAAwE;IAGzE;;OAEG;IAEH,0DA8BC;IAKD;;;;OAIG;IACH,gCAHW,MAAM,GACJ,OAAO,CAQnB;CACD;;;WA3Ga,SAAS,MAAM;;;;;;;;;;;;eAkBf,MAAM;;;;;4BAOP,SAAS,GAAG;IACpB,QAAQ,EAAE,IAAI,CAAC;IACf,YAAY,EAAE,MAAM,CAAA;CACrB;4BAIS,SAAS,GAAG;IACpB,QAAQ,CAAC,EAAE,KAAK,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;oBAIS,aAAa,GAAG,aAAa;0BA1ChB,qBAAqB"}
@@ -1 +1 @@
1
- {"version":3,"file":"required-validator.d.ts","sourceRoot":"","sources":["../../../src/validator/required-validator.js"],"names":[],"mappings":"AAIA;;GAEG;AAEH;;;GAGG;AACH;IAMC;;;OAGG;IACH,2BAFW,MAAM,EAQhB;IAED;;;OAGG;IACH,sBAFW,QAAQ,+CAIlB;CACD;;;0BAnCyB,qBAAqB"}
1
+ {"version":3,"file":"required-validator.d.ts","sourceRoot":"","sources":["../../../src/validator/required-validator.js"],"names":[],"mappings":"AAIA;;GAEG;AAEH;;;GAGG;AACH;IAMC;;;OAGG;IACH,2BAFW,MAAM,EAQhB;IAED;;;OAGG;IACH,sBAFW,QAAQ,+CAIlB;CAKD;;;0BAvCyB,qBAAqB"}