@planning-inspectorate/dynamic-forms 1.15.0 → 1.17.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": "1.15.0",
3
+ "version": "1.17.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",
@@ -44,6 +44,9 @@
44
44
  classes: "govuk-label--l",
45
45
  isPageHeading: true
46
46
  },
47
+ hint: {
48
+ text: question.hint
49
+ },
47
50
  errorMessage: errors[question.fieldName] and {
48
51
  text: errors[question.fieldName].msg
49
52
  },
@@ -0,0 +1,32 @@
1
+ import { body } from 'express-validator';
2
+ import BaseValidator from './base-validator.js';
3
+
4
+ /**
5
+ * Universal validator to ensure the answer to the current question is not the same as another question's answer
6
+ */
7
+ export default class SameAnswerValidator extends BaseValidator {
8
+ /**
9
+ * @param {string[]} fieldNamesToCompare - field name to compare against
10
+ * @param {string} [errorMessage]
11
+ */
12
+
13
+ constructor(fieldNamesToCompare, errorMessage) {
14
+ super();
15
+ this.fieldNamesToCompare = fieldNamesToCompare || [];
16
+ this.errorMessage = errorMessage || 'This answer cannot be the same as another answer';
17
+ }
18
+
19
+ /**
20
+ * validates the questionToCompare and compares it to the answer in the journeyResponse
21
+ * @param questionObj
22
+ */
23
+ validate(questionObj) {
24
+ return body(questionObj.fieldName).custom((value, { req }) => {
25
+ const answers = req?.res?.locals?.journeyResponse?.answers || {};
26
+ if (this.fieldNamesToCompare.some((field) => value === answers[field])) {
27
+ throw new Error(this.errorMessage);
28
+ }
29
+ return true;
30
+ });
31
+ }
32
+ }
@@ -0,0 +1,49 @@
1
+ import { describe, it } from 'node:test';
2
+ import assert from 'node:assert';
3
+ import { validationResult } from 'express-validator';
4
+ import SameAnswerValidator from './same-answer-validator.js';
5
+
6
+ describe('./src/dynamic-forms/validator/same-answer-validator.js', () => {
7
+ it('should return an error if the answer is the same as the compared field', async () => {
8
+ const req = {
9
+ body: { questionTwo: 'Test-answer' },
10
+ res: {
11
+ locals: {
12
+ journeyResponse: {
13
+ answers: { questionOne: 'Test-answer' }
14
+ }
15
+ }
16
+ }
17
+ };
18
+
19
+ const validator = new SameAnswerValidator(['questionOne'], 'Answers must be different');
20
+ const middleware = validator.validate({ fieldName: 'questionTwo' });
21
+
22
+ await middleware(req, {}, () => {});
23
+ const result = validationResult(req);
24
+
25
+ assert.strictEqual(result.isEmpty(), false);
26
+ assert.strictEqual(result.array()[0].msg, 'Answers must be different');
27
+ });
28
+ it('should continue if the answer is different to the compared field', async () => {
29
+ const req = {
30
+ body: { questionTwo: 'Test-answer' },
31
+ res: {
32
+ locals: {
33
+ journeyResponse: {
34
+ answers: { questionOne: 'Test-answer-different' }
35
+ }
36
+ }
37
+ }
38
+ };
39
+
40
+ const validator = new SameAnswerValidator(['questionOne'], 'Answers must be different');
41
+ const middleware = validator.validate({ fieldName: 'questionTwo' });
42
+
43
+ await middleware(req, {}, () => {});
44
+ const result = validationResult(req);
45
+
46
+ assert.strictEqual(result.isEmpty(), true);
47
+ assert.strictEqual(result.array().length, 0);
48
+ });
49
+ });
@@ -16,7 +16,10 @@
16
16
  Which class do you want to travel in?
17
17
  </label>
18
18
  </h1>
19
- <select class="govuk-select" id="travelClass" name="travelClass">
19
+ <div id="travelClass-hint" class="govuk-hint">
20
+
21
+ </div>
22
+ <select class="govuk-select" id="travelClass" name="travelClass" aria-describedby="travelClass-hint">
20
23
  <option value="economy" data-cy="answer-economy">Economy</option>
21
24
  <option value="business" data-cy="answer-business">Business</option>
22
25
  <option value="first" data-cy="answer-first">First Class</option>