@planning-inspectorate/dynamic-forms 1.14.0 → 1.15.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.
Files changed (46) hide show
  1. package/.prettierignore +2 -1
  2. package/README.md +9 -1
  3. package/package.json +7 -2
  4. package/src/components/address/question.js +1 -1
  5. package/src/components/address/question.test.js +3 -3
  6. package/src/components/multi-field-input/index.njk +2 -2
  7. package/src/components/multi-field-input/question.js +5 -1
  8. package/src/components/radio/index.njk +2 -2
  9. package/src/components/select/index.njk +2 -2
  10. package/src/components/text-entry-redact/question.test.js +9 -9
  11. package/src/controller.test.js +2 -1
  12. package/src/index.js +8 -2
  13. package/src/lib/session-answer-store.test.js +1 -1
  14. package/src/questions/question-props.d.ts +5 -24
  15. package/src/questions/question.test.js +1 -1
  16. package/src/section.js +3 -0
  17. package/src/validator/validator.js +4 -1
  18. package/test/check-your-answers.test.js +69 -0
  19. package/test/journey.js +41 -0
  20. package/test/journey.test.js +21 -0
  21. package/test/questions.js +189 -0
  22. package/test/questions.test.js +246 -0
  23. package/test/snapshots/add-insurance.html +44 -0
  24. package/test/snapshots/check-your-answers-not-started.html +201 -0
  25. package/test/snapshots/check-your-answers-with-answers.html +201 -0
  26. package/test/snapshots/companions.html +51 -0
  27. package/test/snapshots/departure-date.html +57 -0
  28. package/test/snapshots/holiday-activities.html +49 -0
  29. package/test/snapshots/holiday-arrival.html +101 -0
  30. package/test/snapshots/holiday-description.html +33 -0
  31. package/test/snapshots/holiday-destination.html +56 -0
  32. package/test/snapshots/holiday-period.html +102 -0
  33. package/test/snapshots/holiday-snack.html +33 -0
  34. package/test/snapshots/hotel-address.html +65 -0
  35. package/test/snapshots/luggage-weight-units.html +75 -0
  36. package/test/snapshots/nights.html +28 -0
  37. package/test/snapshots/secret-wish.html +173 -0
  38. package/test/snapshots/travel-class.html +52 -0
  39. package/test/snapshots/travel-insurance-type.html +56 -0
  40. package/test/utils/app.js +41 -0
  41. package/test/utils/nunjucks.js +20 -0
  42. package/test/utils/test-server.js +167 -0
  43. package/test/utils/test-server.test.js +75 -0
  44. package/{src/lib/test-utils.js → test/utils/utils.js} +15 -16
  45. package/test/views/layout-check-your-answers.njk +1 -0
  46. /package/{src/lib/test-layout.njk → test/views/layout-question.njk} +0 -0
package/.prettierignore CHANGED
@@ -3,4 +3,5 @@ package.json
3
3
  package-lock.json
4
4
  README.md
5
5
  *.njk
6
- .env*
6
+ .env*
7
+ test/snapshots/*
package/README.md CHANGED
@@ -142,4 +142,12 @@ When contributing to this package, ensure changes are generic and not service-sp
142
142
 
143
143
  Commits must follow conventional commits, and the commit types will be used by semantic-release to determine the next version number. For example `feat` commits will result in a minor version bump, while `fix` commits will result in a patch version bump. Major version bumps should be reserved for breaking changes, and should be discussed with the R&D team before being made.
144
144
 
145
- The package will be released automatically using semantic-release, on merge to main. This will include a git tag for the release, and publishing to NPM.
145
+ The package will be released automatically using semantic-release, on merge to main. This will include a git tag for the release, and publishing to NPM.
146
+
147
+ ### Tests
148
+
149
+ There are some lightweight tests in the `test` directory which sets up a basic journey and checks the rendering for each question as well as redirect logic.
150
+
151
+ When adding a new question type, be sure to add an example question into `test/questions.js`, and mock answers into `test/questions.test.js#mockAnswerBody` and `test/questions.test.js#mockAnswer`. Also, the question should be added to the journey in `test/journey.js`.
152
+
153
+ To update any snapshots with rendering changes (or new questions), run `node --test --test-update-snapshots`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@planning-inspectorate/dynamic-forms",
3
- "version": "1.14.0",
3
+ "version": "1.15.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",
@@ -25,13 +25,18 @@
25
25
  "nunjucks": "^3.2.4"
26
26
  },
27
27
  "imports": {
28
- "#question-types": "./src/questions/question-types.d.ts"
28
+ "#question-types": "./src/questions/question-types.d.ts",
29
+ "#test/*": "./test/*"
29
30
  },
30
31
  "devDependencies": {
31
32
  "@commitlint/cli": "^20.0.0",
32
33
  "@commitlint/config-conventional": "^20.0.0",
34
+ "body-parser": "^2.2.0",
35
+ "cookie-parser": "^1.4.7",
33
36
  "eslint": "^9.29.0",
34
37
  "eslint-config-prettier": "^10.1.5",
38
+ "express": "^5.1.0",
39
+ "express-session": "^1.18.2",
35
40
  "govuk-frontend": "^5.11.0",
36
41
  "husky": "^9.1.7",
37
42
  "lint-staged": "^16.1.2",
@@ -24,7 +24,7 @@ export default class AddressQuestion extends Question {
24
24
  viewFolder: 'address'
25
25
  });
26
26
 
27
- for (const validator of params.validators) {
27
+ for (const validator of params.validators || []) {
28
28
  if (validator instanceof AddressValidator) {
29
29
  this.requiredFields = validator.requiredFields;
30
30
  }
@@ -3,7 +3,7 @@ import assert from 'node:assert';
3
3
  import AddressQuestion from './question.js';
4
4
  import { Address } from '../../lib/address.js';
5
5
  import AddressValidator from '../../validator/address-validator.js';
6
- import { configureNunjucks } from '../../lib/test-utils.js';
6
+ import { configureNunjucksTestEnv } from '../../../test/utils/nunjucks.js';
7
7
 
8
8
  describe('AddressQuestion', () => {
9
9
  const TITLE = 'What is the site address?';
@@ -132,12 +132,12 @@ describe('AddressQuestion', () => {
132
132
  const model = await question.prepQuestionForRendering(
133
133
  {},
134
134
  { getBackLink: mock.fn(), response },
135
- { layoutTemplate: 'lib/test-layout.njk' }
135
+ { layoutTemplate: 'views/layout-question.njk' }
136
136
  );
137
137
 
138
138
  assert.strictEqual(model.question.hint, hintText);
139
139
 
140
- const nunjucks = configureNunjucks();
140
+ const nunjucks = configureNunjucksTestEnv();
141
141
  const mockRes = {
142
142
  render: mock.fn((view, data) => nunjucks.render(view + '.njk', data))
143
143
  };
@@ -39,11 +39,11 @@
39
39
  describedBy: question.hint and "multi-field-hint"
40
40
  }) %}
41
41
 
42
- {% if question.hint %}
42
+ {%- if question.hint -%}
43
43
  <div id="multi-field-hint" class="govuk-hint">
44
44
  {{ question.hint }}
45
45
  </div>
46
- {% endif %}
46
+ {%- endif %}
47
47
 
48
48
  {% if question.description %}
49
49
  <p class="govuk-body">
@@ -88,7 +88,11 @@ export default class MultiFieldInputQuestion extends Question {
88
88
  let responseToSave = { answers: {} };
89
89
 
90
90
  for (const inputField of this.inputFields) {
91
- responseToSave.answers[inputField.fieldName] = req.body[inputField.fieldName]?.trim();
91
+ let value = req.body[inputField.fieldName];
92
+ if (typeof value === 'string') {
93
+ value = value.trim();
94
+ }
95
+ responseToSave.answers[inputField.fieldName] = value;
92
96
  journeyResponse.answers[inputField.fieldName] = responseToSave.answers[inputField.fieldName];
93
97
  }
94
98
 
@@ -39,7 +39,7 @@
39
39
  classes: "govuk-radios",
40
40
  idPrefix: question.fieldName,
41
41
  name: question.fieldName,
42
- hint: { text: question.hint },
42
+ hint: question.hint and { text: question.hint },
43
43
  errorMessage: errors[question.fieldName] and {
44
44
  text: errors[question.fieldName].msg
45
45
  },
@@ -72,7 +72,7 @@
72
72
  classes: "govuk-radios",
73
73
  idPrefix: question.fieldName,
74
74
  name: question.fieldName,
75
- hint: { text: question.hint },
75
+ hint: question.hint and { text: question.hint },
76
76
  errorMessage: errors[question.fieldName] and {
77
77
  text: errors[question.fieldName].msg
78
78
  },
@@ -84,10 +84,10 @@
84
84
  let input = document.querySelector('#{{ question.fieldName }}');
85
85
  let select = document.querySelector('#{{ question.fieldName }}-select');
86
86
  let options = Array.from(select.options);
87
- if (!input.value || !options.some(option => option.value === input.value)) {
87
+ if (!input.value || !options.some(option => option.innerText === input.value)) {
88
88
  select.options.selectedIndex = -1;
89
89
  } else {
90
- select.options.selectedIndex = options.findIndex(option => option.value === input.value);
90
+ select.options.selectedIndex = options.findIndex(option => option.innerText === input.value);
91
91
  }
92
92
  }
93
93
  })
@@ -1,7 +1,7 @@
1
1
  import { describe, it, mock } from 'node:test';
2
2
  import assert from 'node:assert';
3
3
  import TextEntryRedactQuestion from './question.js';
4
- import { configureNunjucks } from '../../lib/test-utils.js';
4
+ import { configureNunjucksTestEnv } from '../../../test/utils/nunjucks.js';
5
5
 
6
6
  describe('./src/dynamic-forms/components/text-entry-redact/question.js', () => {
7
7
  const TITLE = 'title';
@@ -55,7 +55,7 @@ describe('./src/dynamic-forms/components/text-entry-redact/question.js', () => {
55
55
  }
56
56
  };
57
57
  const customViewData = {
58
- layoutTemplate: 'lib/test-layout.njk',
58
+ layoutTemplate: 'views/layout-question.njk',
59
59
  question: {
60
60
  question: 'Redaction Question',
61
61
  fieldName: 'field-name',
@@ -63,7 +63,7 @@ describe('./src/dynamic-forms/components/text-entry-redact/question.js', () => {
63
63
  valueRedacted: 'value-redacted'
64
64
  }
65
65
  };
66
- const nunjucks = configureNunjucks();
66
+ const nunjucks = configureNunjucksTestEnv();
67
67
  const mockRes = {
68
68
  render: mock.fn((view, data) => nunjucks.render(view + '.njk', data))
69
69
  };
@@ -94,7 +94,7 @@ describe('./src/dynamic-forms/components/text-entry-redact/question.js', () => {
94
94
  }
95
95
  };
96
96
  const customViewData = {
97
- layoutTemplate: 'lib/test-layout.njk',
97
+ layoutTemplate: 'views/layout-question.njk',
98
98
  question: {
99
99
  question: 'Redaction Question',
100
100
  fieldName: 'field-name',
@@ -102,7 +102,7 @@ describe('./src/dynamic-forms/components/text-entry-redact/question.js', () => {
102
102
  valueRedacted: 'value-redacted'
103
103
  }
104
104
  };
105
- const nunjucks = configureNunjucks();
105
+ const nunjucks = configureNunjucksTestEnv();
106
106
  const mockRes = {
107
107
  render: mock.fn((view, data) => nunjucks.render(view + '.njk', data))
108
108
  };
@@ -134,7 +134,7 @@ describe('./src/dynamic-forms/components/text-entry-redact/question.js', () => {
134
134
  }
135
135
  };
136
136
  const customViewData = {
137
- layoutTemplate: 'lib/test-layout.njk',
137
+ layoutTemplate: 'views/layout-question.njk',
138
138
  question: {
139
139
  question: 'Redaction Question',
140
140
  fieldName: 'field-name',
@@ -147,7 +147,7 @@ describe('./src/dynamic-forms/components/text-entry-redact/question.js', () => {
147
147
  { category: 'Address', suggestion: '123 Fake Street' }
148
148
  ]
149
149
  };
150
- const nunjucks = configureNunjucks();
150
+ const nunjucks = configureNunjucksTestEnv();
151
151
  const mockRes = {
152
152
  render: mock.fn((view, data) => nunjucks.render(view + '.njk', data))
153
153
  };
@@ -181,7 +181,7 @@ describe('./src/dynamic-forms/components/text-entry-redact/question.js', () => {
181
181
  }
182
182
  };
183
183
  const customViewData = {
184
- layoutTemplate: 'lib/test-layout.njk',
184
+ layoutTemplate: 'views/layout-question.njk',
185
185
  question: {
186
186
  question: 'Redaction Question',
187
187
  fieldName: 'field-name',
@@ -193,7 +193,7 @@ describe('./src/dynamic-forms/components/text-entry-redact/question.js', () => {
193
193
  { category: 'Address', suggestion: '123 Fake Street' }
194
194
  ]
195
195
  };
196
- const nunjucks = configureNunjucks();
196
+ const nunjucks = configureNunjucksTestEnv();
197
197
  const mockRes = {
198
198
  render: mock.fn((view, data) => nunjucks.render(view + '.njk', data))
199
199
  };
@@ -4,7 +4,8 @@ import { list, question, buildSave } from './controller.js';
4
4
 
5
5
  import { Journey } from './journey/journey.js';
6
6
  import { SECTION_STATUS } from './section.js';
7
- import { mockReq, mockRes } from './lib/test-utils.js';
7
+
8
+ import { mockReq, mockRes } from '#test/utils/utils.js';
8
9
 
9
10
  const res = mockRes();
10
11
  const mockBaseUrl = '/manage-appeals/questionnaire';
package/src/index.js CHANGED
@@ -1,3 +1,10 @@
1
+ /**
2
+ * @typedef {import('./questions/question-props.js').QuestionTypes} QuestionTypes
3
+ */
4
+
5
+ /**
6
+ * @type {Readonly<{CHECKBOX: QuestionTypes, BOOLEAN: QuestionTypes, RADIO: QuestionTypes, DATE: QuestionTypes, DATE_PERIOD: QuestionTypes, TEXT_ENTRY: QuestionTypes, TEXT_ENTRY_REDACT: QuestionTypes, SELECT: QuestionTypes, SINGLE_LINE_INPUT: QuestionTypes, MULTI_FIELD_INPUT: QuestionTypes, NUMBER: QuestionTypes, ADDRESS: QuestionTypes, UNIT_OPTION: QuestionTypes}>}
7
+ */
1
8
  export const COMPONENT_TYPES = Object.freeze({
2
9
  CHECKBOX: 'checkbox',
3
10
  BOOLEAN: 'boolean',
@@ -12,6 +19,5 @@ export const COMPONENT_TYPES = Object.freeze({
12
19
  MULTI_FIELD_INPUT: 'multi-field-input',
13
20
  NUMBER: 'number',
14
21
  ADDRESS: 'site-address',
15
- UNIT_OPTION: 'unit-option',
16
- LIST_ADD_MORE: 'list-add-more'
22
+ UNIT_OPTION: 'unit-option'
17
23
  });
@@ -5,8 +5,8 @@ import {
5
5
  clearDataFromSession,
6
6
  buildSaveDataToSession
7
7
  } from './session-answer-store.js';
8
- import { mockReq, mockRes } from './test-utils.js';
9
8
  import { BOOLEAN_OPTIONS } from '../components/boolean/question.js';
9
+ import { mockReq, mockRes } from '#test/utils/utils.js';
10
10
 
11
11
  describe('session-answer-store', () => {
12
12
  describe('saveDataToSession', () => {
@@ -8,18 +8,15 @@ type QuestionTypes =
8
8
  | 'boolean'
9
9
  | 'radio'
10
10
  | 'date'
11
+ | 'date-period'
11
12
  | 'text-entry'
13
+ | 'text-entry-redact'
14
+ | 'select'
12
15
  | 'single-line-input'
13
16
  | 'multi-field-input'
14
17
  | 'number'
15
18
  | 'site-address'
16
- | 'unit-option'
17
- | 'list-add-more'
18
- // strictly for add more sub question type
19
- // todo refactor list add mores so there's no sub question
20
- | 'case'
21
- | 'address'
22
- | 'listed-building';
19
+ | 'unit-option';
23
20
 
24
21
  interface CommonQuestionProps {
25
22
  type: QuestionTypes;
@@ -155,21 +152,6 @@ type UnitOptionEntryQuestionProps = CommonQuestionProps & {
155
152
  label?: string;
156
153
  };
157
154
 
158
- export type SubQuestionProps = CommonQuestionProps & {
159
- type: 'case' | 'address' | 'listed-building';
160
- viewFolder: string;
161
- };
162
-
163
- type ListAddMoreQuestionProps = CommonQuestionProps & {
164
- type: 'list-add-more';
165
- subQuestionProps: SubQuestionProps;
166
- subQuestionLabel?: string;
167
- subQuestionTitle?: string;
168
- subQuestionFieldLabel?: string;
169
- subQuestionInputClasses?: string;
170
- width?: string;
171
- };
172
-
173
155
  export type QuestionProps =
174
156
  | CheckboxQuestionProps
175
157
  | MultiFileUploadQuestionProps
@@ -181,5 +163,4 @@ export type QuestionProps =
181
163
  | MultiFieldInputQuestionProps
182
164
  | NumberEntryQuestionProps
183
165
  | SiteAddressQuestionProps
184
- | UnitOptionEntryQuestionProps
185
- | ListAddMoreQuestionProps;
166
+ | UnitOptionEntryQuestionProps;
@@ -1,7 +1,7 @@
1
1
  import { describe, it, mock } from 'node:test';
2
2
  import assert from 'node:assert';
3
- import { mockRes } from '../lib/test-utils.js';
4
3
  import { Question } from './question.js';
4
+ import { mockRes } from '../../test/utils/utils.js';
5
5
 
6
6
  const res = mockRes();
7
7
 
package/src/section.js CHANGED
@@ -80,6 +80,9 @@ export class Section {
80
80
  * @returns {Section}
81
81
  */
82
82
  addQuestion(question) {
83
+ if (!question) {
84
+ throw new Error('question is required');
85
+ }
83
86
  this.questions.push(question);
84
87
  this.#conditionAdded = false; // reset condition flag
85
88
  this.#applyConditions(question);
@@ -12,7 +12,10 @@ const validate = async (req, res, next) => {
12
12
  }
13
13
  if (req.body && typeof req.body === 'object' && req.body.constructor === Object) {
14
14
  for (const key of Object.keys(req.body)) {
15
- req.body[key] = req.body[key].trim();
15
+ if (typeof req.body[key] === 'string') {
16
+ // Trim whitespace from string values in req.body
17
+ req.body[key] = req.body[key].trim();
18
+ }
16
19
  }
17
20
  } else {
18
21
  throw new Error(`req.body is not an object: ${req.body} :: ${typeof req.body}`);
@@ -0,0 +1,69 @@
1
+ import { describe, it } from 'node:test';
2
+ import assert from 'assert';
3
+ import { questionsInOrder } from './questions.js';
4
+ import path from 'path';
5
+ import { escapeForRegExp, snapshotsDir } from './utils/utils.js';
6
+ import { createAppWithQuestions, mockAnswer, mockAnswerBody } from './questions.test.js';
7
+
8
+ describe('check-your-answers', () => {
9
+ it(`should render all question rows with no answers`, async (ctx) => {
10
+ const testServer = await createAppWithQuestions(ctx);
11
+
12
+ const response = await testServer.get('/check-your-answers');
13
+ assert.strictEqual(response.status, 200);
14
+ const text = await response.text();
15
+ const rows = text.split('<div class="govuk-summary-list__row">').slice(1); // Skip the first split part which is before the first row
16
+
17
+ assert.strictEqual(rows.length, questionsInOrder.length, 'Expected one row per question');
18
+
19
+ for (let i = 0; i < questionsInOrder.length; i++) {
20
+ const q = questionsInOrder[i];
21
+ const row = rows[i];
22
+
23
+ const escapedTitle = escapeForRegExp(q.title);
24
+ const escapedQ = escapeForRegExp(q.question);
25
+ assert.match(row, new RegExp(`<dt[^>]*>\\s*${escapedTitle}\\s*</dt>`, 'i'));
26
+ assert.match(row, new RegExp(`<dd[^>]*>\\s*Not started\\s*</dd>`, 'i'));
27
+ assert.match(
28
+ row,
29
+ new RegExp(`<a[^>]* href="questions/${q.url}">\\s*Answer\\s*<span[^>]*>\\s*${escapedQ}\\s*</span></a>`, 'i')
30
+ );
31
+ }
32
+ ctx.assert.fileSnapshot(text, path.join(snapshotsDir(), 'check-your-answers-not-started.html'), {
33
+ serializers: [(v) => v]
34
+ });
35
+ });
36
+
37
+ it(`should render all question answers`, async (ctx) => {
38
+ const testServer = await createAppWithQuestions(ctx);
39
+
40
+ // 'answer' each question
41
+ for (const q of questionsInOrder) {
42
+ await testServer.post(`/questions/${q.url}`, mockAnswerBody(q), { redirect: 'manual' });
43
+ }
44
+
45
+ const response = await testServer.get('/check-your-answers');
46
+ assert.strictEqual(response.status, 200);
47
+ const text = await response.text();
48
+ const rows = text.split('<div class="govuk-summary-list__row">').slice(1); // Skip the first split part which is before the first row
49
+
50
+ assert.strictEqual(rows.length, questionsInOrder.length, 'Expected one row per question');
51
+
52
+ for (let i = 0; i < questionsInOrder.length; i++) {
53
+ const q = questionsInOrder[i];
54
+ const row = rows[i];
55
+
56
+ const escapedTitle = escapeForRegExp(q.title);
57
+ const escapedQ = escapeForRegExp(q.question);
58
+ assert.match(row, new RegExp(`<dt[^>]*>\\s*${escapedTitle}\\s*</dt>`, 'i'));
59
+ assert.match(row, new RegExp(`<dd[^>]*>\\s*${mockAnswer(q)}\\s*</dd>`, 'i'));
60
+ assert.match(
61
+ row,
62
+ new RegExp(`<a[^>]* href="questions/${q.url}">\\s*Change\\s*<span[^>]*>\\s*${escapedQ}\\s*</span></a>`, 'i')
63
+ );
64
+ }
65
+ ctx.assert.fileSnapshot(text, path.join(snapshotsDir(), 'check-your-answers-with-answers.html'), {
66
+ serializers: [(v) => v]
67
+ });
68
+ });
69
+ });
@@ -0,0 +1,41 @@
1
+ import { Section } from '../src/section.js';
2
+ import { Journey } from '../src/journey/journey.js';
3
+
4
+ export const JOURNEY_ID = 'holiday-journey';
5
+
6
+ /**
7
+ * @param {{[questionType: string]: import('../src/questions/question.js').Question}} questions
8
+ * @param {import('../src/journey/journey-response.js').JourneyResponse} response
9
+ * @returns {Journey}
10
+ */
11
+ export function createJourney(questions, response) {
12
+ return new Journey({
13
+ journeyId: JOURNEY_ID,
14
+ sections: [
15
+ new Section('Holiday Details', 'questions')
16
+ .addQuestion(questions.holidayActivities)
17
+ .addQuestion(questions.addInsurance)
18
+ .addQuestion(questions.travelInsuranceType)
19
+ .addQuestion(questions.holidayDestination)
20
+ .addQuestion(questions.departureDate)
21
+ .addQuestion(questions.holidayPeriod)
22
+ .addQuestion(questions.holidayDescription)
23
+ .addQuestion(questions.secretWish)
24
+ .addQuestion(questions.travelClass)
25
+ .addQuestion(questions.holidayArrival)
26
+ .addQuestion(questions.holidaySnack)
27
+ .addQuestion(questions.companions)
28
+ .addQuestion(questions.nights)
29
+ .addQuestion(questions.hotelAddress)
30
+ .addQuestion(questions.luggageWeight)
31
+ ],
32
+ taskListUrl: 'check-your-answers',
33
+ journeyTemplate: 'views/layout-question.njk',
34
+ listingPageViewPath: 'views/layout-check-your-answers.njk',
35
+ journeyTitle: 'Holiday Booking',
36
+ returnToListing: false,
37
+ makeBaseUrl: () => '/',
38
+ initialBackLink: '/holidays',
39
+ response
40
+ });
41
+ }
@@ -0,0 +1,21 @@
1
+ import { describe, it } from 'node:test';
2
+ import assert from 'assert';
3
+ import { createJourney } from './journey.js';
4
+ import { getQuestions } from './questions.js';
5
+
6
+ // Mock response object (empty for this test)
7
+ const response = {};
8
+
9
+ describe('Journey', function () {
10
+ it('should have all questions defined on the journey', function () {
11
+ // Use the questions from the questions module
12
+ const questions = getQuestions();
13
+ const journey = createJourney(questions, response);
14
+ const journeyQuestions = journey.sections[0].questions;
15
+ let previousQuestion = null;
16
+ for (const question of journeyQuestions) {
17
+ assert.ok(question, `Question is not defined, after: ${previousQuestion && previousQuestion.title}`);
18
+ previousQuestion = question;
19
+ }
20
+ });
21
+ });