@planning-inspectorate/dynamic-forms 3.1.0 → 3.3.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 (31) hide show
  1. package/README.md +9 -9
  2. package/package.json +1 -1
  3. package/src/components/manage-list/README.md +21 -2
  4. package/src/components/manage-list/index.njk +3 -3
  5. package/src/components/manage-list/question.js +22 -10
  6. package/src/components/manage-list/utils.js +14 -0
  7. package/src/components/manage-list/utils.test.js +47 -0
  8. package/src/components/utils/question-has-answer.js +45 -11
  9. package/src/components/utils/question-utils.js +3 -37
  10. package/src/controller.js +4 -0
  11. package/src/controller.test.js +5 -1
  12. package/src/journey/journey.js +1 -0
  13. package/src/lib/session-answer-store.js +12 -3
  14. package/src/lib/session-answer-store.test.js +42 -0
  15. package/src/lib/utils.js +11 -0
  16. package/src/lib/utils.test.js +19 -0
  17. package/src/questions/question.js +9 -9
  18. package/src/questions/question.test.js +2 -0
  19. package/src/section-types.d.ts +2 -0
  20. package/src/section.js +5 -1
  21. package/src/section.test.js +7 -3
  22. package/test/check-your-answers.test.js +19 -1
  23. package/test/questions.test.js +3 -3
  24. package/src/components/macros/answer-list.njk +0 -29
  25. package/src/middleware/check-not-submitted.js +0 -13
  26. package/src/middleware/check-not-submitted.test.js +0 -118
  27. package/src/middleware/dynamic-req-files-to-req-body-files.js +0 -46
  28. package/src/middleware/dynamic-req-files-to-req-body-files.test.js +0 -221
  29. package/src/validator/required-file-upload-validator.js +0 -38
  30. package/src/validator/required-file-upload-validator.test.js +0 -141
  31. package/src/validator/schemas/required-file-upload-schema.js +0 -35
@@ -715,12 +715,14 @@ describe('./src/dynamic-forms/section.js', () => {
715
715
  const section = new Section('s1', 'S').addQuestion(q1).addQuestion(manageListQ, manageListSection);
716
716
  return { section, q1, manageListQ, q3, q4 };
717
717
  };
718
+ const response = { answers: {} };
718
719
  it(`should get first question within a manage list section`, () => {
719
720
  const { section, manageListQ, q3 } = newSectionWithManageList();
720
721
  const next = section.getNextQuestion({
721
722
  questionFieldName: manageListQ.fieldName,
722
- response: {},
723
+ response,
723
724
  manageListQuestion: manageListQ,
725
+ routeParams: {},
724
726
  takeNextQuestion: true,
725
727
  reverse: false
726
728
  });
@@ -730,8 +732,9 @@ describe('./src/dynamic-forms/section.js', () => {
730
732
  const { section, manageListQ, q3, q4 } = newSectionWithManageList();
731
733
  const next = section.getNextQuestion({
732
734
  questionFieldName: q3.fieldName,
733
- response: {},
735
+ response,
734
736
  manageListQuestion: manageListQ,
737
+ routeParams: {},
735
738
  takeNextQuestion: false,
736
739
  reverse: false
737
740
  });
@@ -741,8 +744,9 @@ describe('./src/dynamic-forms/section.js', () => {
741
744
  const { section, manageListQ, q4 } = newSectionWithManageList();
742
745
  const next = section.getNextQuestion({
743
746
  questionFieldName: q4.fieldName,
744
- response: {},
747
+ response,
745
748
  manageListQuestion: manageListQ,
749
+ routeParams: {},
746
750
  takeNextQuestion: false,
747
751
  reverse: false
748
752
  });
@@ -1,9 +1,10 @@
1
1
  import { describe, it } from 'node:test';
2
2
  import assert from 'assert';
3
- import { questionsInOrder } from './questions.js';
3
+ import { manageListQuestions, questionsInOrder } from './questions.js';
4
4
  import path from 'path';
5
5
  import { escapeForRegExp, snapshotsDir } from './utils/utils.js';
6
6
  import { createAppWithQuestions, mockAnswer, mockAnswerBody } from './questions.test.js';
7
+ import { COMPONENT_TYPES } from '#src/index.js';
7
8
 
8
9
  describe('check-your-answers', () => {
9
10
  it(`should render all question rows with no answers`, async (ctx) => {
@@ -39,6 +40,23 @@ describe('check-your-answers', () => {
39
40
 
40
41
  // 'answer' each question
41
42
  for (const q of questionsInOrder) {
43
+ // Save happens on subquestion for manage list questions
44
+ if (q.type === COMPONENT_TYPES.MANAGE_LIST) {
45
+ // Get the list of subquestions for this manage list question
46
+ const manageListQuestion = manageListQuestions.find(
47
+ (question) => question.manageListQuestion.fieldName === q.fieldName
48
+ );
49
+ const subQuestionAnswers = mockAnswerBody(q)[manageListQuestion.manageListQuestion.fieldName];
50
+ // Post to them individually
51
+ for (const answer of subQuestionAnswers) {
52
+ const id = answer.id;
53
+ for (const subQuestion of manageListQuestion.questions) {
54
+ const key = subQuestion.fieldName;
55
+ const body = { [key]: answer[key] };
56
+ await testServer.post(`/questions/${q.url}/add/${id}/${subQuestion.url}`, body, { redirect: 'manual' });
57
+ }
58
+ }
59
+ }
42
60
  await testServer.post(`/questions/${q.url}`, mockAnswerBody(q), { redirect: 'manual' });
43
61
  }
44
62
 
@@ -257,9 +257,9 @@ export function mockAnswerBody(q) {
257
257
  // manage list questions not available here so just hard coded
258
258
  return {
259
259
  [q.fieldName]: [
260
- { travelCompanionName: 'Companion 1', travelCompanionEmail: 'compaion-1@example.com' },
261
- { travelCompanionName: 'Companion 2', travelCompanionEmail: 'compaion-2@example.com' },
262
- { travelCompanionName: 'Companion 3', travelCompanionEmail: 'compaion-3@example.com' }
260
+ { id: '1234', travelCompanionName: 'Companion 1', travelCompanionEmail: 'companion-1@example.com' },
261
+ { id: '4567', travelCompanionName: 'Companion 2', travelCompanionEmail: 'companion-2@example.com' },
262
+ { id: '8901', travelCompanionName: 'Companion 3', travelCompanionEmail: 'companion-3@example.com' }
263
263
  ]
264
264
  };
265
265
  }
@@ -1,29 +0,0 @@
1
- {% from "govuk/components/summary-list/macro.njk" import govukSummaryList %}
2
-
3
- {% macro answerList(answers) %}
4
- {% set summaryRows = [] %}
5
-
6
- {% for item in answers %}
7
- {% set summaryRows = (summaryRows.push({
8
- key: {
9
- text: item.label
10
- },
11
- value: {
12
- html: item.answer | escape | nl2br | safe
13
- },
14
- actions: {
15
- items: [
16
- {
17
- href: item.removeLink,
18
- text: "Remove",
19
- visuallyHiddenText: item.label + ", " + item.answer
20
- }
21
- ]
22
- }
23
- }), summaryRows) %}
24
- {% endfor %}
25
-
26
- {{ govukSummaryList({
27
- rows: summaryRows
28
- }) }}
29
- {% endmacro %}
@@ -1,13 +0,0 @@
1
- /**
2
- * @param {string} alreadySubmittedUrl
3
- * @returns {import('express').Handler}
4
- */
5
- export default (alreadySubmittedUrl) => (req, res, next) => {
6
- const journeyResponse = res?.locals?.journeyResponse;
7
-
8
- if (journeyResponse?.answers?.submitted === 'yes' || journeyResponse?.answers?.submitted === true) {
9
- return res.redirect(alreadySubmittedUrl);
10
- }
11
-
12
- return next();
13
- };
@@ -1,118 +0,0 @@
1
- import { describe, it, mock } from 'node:test';
2
- import assert from 'node:assert';
3
- import checkNotSubmitted from './check-not-submitted.js';
4
-
5
- const testUrl = '/test';
6
-
7
- describe('dynamic-forms/middleware/check-not-submitted', () => {
8
- const mockReq = () => ({
9
- cookies: {},
10
- log: {},
11
- params: {}
12
- });
13
-
14
- [
15
- {
16
- description: 'should do nothing if submitted is undefined',
17
- given: () => {
18
- return {
19
- req: mockReq(),
20
- res: {}
21
- };
22
- },
23
- expected: (req, res, next) => {
24
- assert.strictEqual(next.mock.callCount(), 1);
25
- }
26
- },
27
- {
28
- description: 'should do nothing if submitted is false',
29
- given: () => {
30
- return {
31
- req: mockReq(),
32
- res: {
33
- locals: {
34
- journeyResponse: {
35
- answers: { submitted: false }
36
- }
37
- }
38
- }
39
- };
40
- },
41
- expected: (req, res, next) => {
42
- assert.strictEqual(next.mock.callCount(), 1);
43
- }
44
- },
45
- {
46
- description: 'should do nothing if submitted is no',
47
- given: () => {
48
- return {
49
- req: mockReq(),
50
- res: {
51
- locals: {
52
- journeyResponse: {
53
- answers: {
54
- submitted: 'no'
55
- }
56
- }
57
- }
58
- }
59
- };
60
- },
61
- expected: (req, res, next) => {
62
- assert.strictEqual(next.mock.callCount(), 1);
63
- }
64
- },
65
- {
66
- description: 'should redirect if submitted is true',
67
- given: () => {
68
- return {
69
- req: mockReq(),
70
- res: {
71
- locals: {
72
- journeyResponse: {
73
- answers: {
74
- submitted: true
75
- }
76
- }
77
- },
78
- redirect: mock.fn()
79
- }
80
- };
81
- },
82
- expected: (req, res) => {
83
- assert.strictEqual(res.redirect.mock.callCount(), 1);
84
- }
85
- },
86
- {
87
- description: 'should redirect if submitted is yes',
88
- given: () => {
89
- return {
90
- req: mockReq(),
91
- res: {
92
- locals: {
93
- journeyResponse: {
94
- answers: {
95
- submitted: 'yes'
96
- }
97
- }
98
- },
99
- redirect: mock.fn()
100
- }
101
- };
102
- },
103
- expected: (req, res) => {
104
- assert.strictEqual(res.redirect.mock.callCount(), 1);
105
- }
106
- }
107
- ].forEach(({ description, given, expected }) => {
108
- it(description, () => {
109
- const next = mock.fn();
110
- const req = given().req;
111
- const res = given().res;
112
-
113
- checkNotSubmitted(testUrl)(req, res, next);
114
-
115
- expected(req, res, next);
116
- });
117
- });
118
- });
@@ -1,46 +0,0 @@
1
- /**
2
- * Middleware for converting `req.files` to `req.body.files`.
3
- *
4
- * The requirement for this is due to `express-validator` only considering a limited subset of
5
- * keys on the `req` object as being eligible for validation.
6
- *
7
- * This middleware is designed for use with component-based dynamic forms
8
- *
9
- * This has not been tested with
10
- * pages that contain two or more multiple file uploads.
11
- *
12
- * @returns {function(*, *, *): (*)}
13
- */
14
- export default () => (req, res, next) => {
15
- if (!req.body || !req.files || !req.params) {
16
- return next();
17
- }
18
-
19
- const { journey } = res.locals;
20
-
21
- const questionObj = journey.getQuestionByParams(req.params);
22
- const filesPropertyPath = questionObj.fieldName;
23
-
24
- if (typeof req.files[filesPropertyPath] === 'undefined') {
25
- req.body.files = [];
26
- return next();
27
- }
28
-
29
- try {
30
- req.body.files = {
31
- ...req.body.files,
32
- // Ensure we are always working with an array. Single files would otherwise be an object.
33
- [filesPropertyPath]: Array.isArray(req.files[filesPropertyPath])
34
- ? req.files[filesPropertyPath]
35
- : [req.files[filesPropertyPath]]
36
- };
37
- } catch (err) {
38
- // this feels like it should never happen, as nothing in the try block throws. This is a precaution.
39
- /* istanbul ignore next */
40
- req.log.debug({ err, filesPropertyPath }, 'Error extracting req.files[filesPropertyPath]');
41
- /* istanbul ignore next */
42
- req.body.files = [];
43
- }
44
-
45
- return next();
46
- };
@@ -1,221 +0,0 @@
1
- import { describe, it, mock, beforeEach } from 'node:test';
2
- import assert from 'node:assert';
3
- import dynamicReqFilesToReqBodyFilesMiddleware from './dynamic-req-files-to-req-body-files.js';
4
-
5
- describe('dynamic-forms/middleware/dynamic-req-files-to-req-body-files', () => {
6
- let counter = 0;
7
- let mockReq;
8
- let mockRes;
9
- let filesPropertyPath = `example-files-property-path-${counter}`;
10
-
11
- const mockgetQuestionByParams = mock.fn(() => {
12
- return { fieldName: filesPropertyPath };
13
- });
14
-
15
- beforeEach(() => {
16
- mockReq = () => ({
17
- cookies: {},
18
- log: {},
19
- params: {}
20
- });
21
-
22
- mockRes = () => ({
23
- clearCookie: mock.fn(),
24
- cookie: mock.fn(),
25
- locals: {
26
- journeyResponse: mock.fn(),
27
- journey: { getQuestionByParams: mockgetQuestionByParams }
28
- },
29
- redirect: mock.fn(),
30
- render: mock.fn(),
31
- sendStatus: mock.fn(),
32
- status: mock.fn()
33
- });
34
-
35
- counter++;
36
- filesPropertyPath = `example-files-property-path-${counter}`;
37
- });
38
-
39
- [
40
- {
41
- description: 'should return early if `req.body` is not set',
42
- given: () => mockReq(),
43
- expected: (req, res, next) => {
44
- assert.strictEqual(req.body, undefined);
45
- assert.strictEqual(next.mock.callCount(), 1);
46
- }
47
- },
48
- {
49
- description: 'should return early if `req.files` is not set',
50
- given: () => ({
51
- ...mockReq(),
52
- body: {}
53
- }),
54
- expected: (req, res, next) => {
55
- assert.strictEqual(req.body.files, undefined);
56
- assert.strictEqual(next.mock.callCount(), 1);
57
- }
58
- },
59
- {
60
- description: 'should return early if `req.params` is not set',
61
- given: () => ({
62
- cookies: {},
63
- log: {},
64
- body: {},
65
- files: {}
66
- }),
67
- expected: (req, res, next) => {
68
- assert.strictEqual(req.body.files, undefined);
69
- assert.strictEqual(next.mock.callCount(), 1);
70
- }
71
- },
72
- {
73
- description:
74
- 'should set `req.body.files = []` if unable to find the given file property path `req.files` - unset',
75
- given: () => ({
76
- ...mockReq(),
77
- body: {},
78
- files: {},
79
- params: {
80
- section: 'mock-section',
81
- question: 'mock-question'
82
- }
83
- }),
84
- expected: (req, res, next) => {
85
- assert.deepStrictEqual(req.body.files, []);
86
- assert.strictEqual(next.mock.callCount(), 1);
87
- }
88
- },
89
- {
90
- description:
91
- 'should set `req.body.files = []` if unable to find the given file property path `req.files` - wrong path',
92
- given: () => ({
93
- ...mockReq(),
94
- body: {},
95
- files: {
96
- 'some-different-path': {
97
- x: 'y'
98
- }
99
- },
100
- params: {
101
- section: 'mock-section',
102
- question: 'mock-question'
103
- }
104
- }),
105
- expected: (req, res, next) => {
106
- assert.deepStrictEqual(req.body.files, []);
107
- assert.strictEqual(next.mock.callCount(), 1);
108
- }
109
- },
110
- {
111
- description: 'should set `req.body.files = [req.files]` when given a single file',
112
- given: () => ({
113
- ...mockReq(),
114
- body: {},
115
- params: {
116
- section: 'mock-section',
117
- question: 'mock-question'
118
- },
119
- files: {
120
- [filesPropertyPath]: {
121
- a: 'b'
122
- }
123
- }
124
- }),
125
- expected: (req, res, next) => {
126
- assert.deepStrictEqual(req.body.files, { [filesPropertyPath]: [{ a: 'b' }] });
127
- assert.strictEqual(next.mock.callCount(), 1);
128
- }
129
- },
130
- {
131
- description: 'should set `req.body.files = [...req.files]` when given multiple files',
132
- given: () => ({
133
- ...mockReq(),
134
- body: {},
135
- params: {
136
- section: 'mock-section',
137
- question: 'mock-question'
138
- },
139
- files: {
140
- [filesPropertyPath]: [
141
- {
142
- a: 'b'
143
- },
144
- {
145
- c: 'd'
146
- }
147
- ]
148
- }
149
- }),
150
- expected: (req, res, next) => {
151
- assert.deepStrictEqual(req.body.files, {
152
- [filesPropertyPath]: [
153
- {
154
- a: 'b'
155
- },
156
- {
157
- c: 'd'
158
- }
159
- ]
160
- });
161
- assert.strictEqual(next.mock.callCount(), 1);
162
- }
163
- },
164
- {
165
- description: 'should retain existing req.body.files when working with a different key',
166
- given: () => ({
167
- ...mockReq(),
168
- body: {
169
- files: {
170
- 'an-existing-key-value-pair': [
171
- {
172
- rgb: 'xyz'
173
- }
174
- ]
175
- }
176
- },
177
- files: {
178
- [filesPropertyPath]: [
179
- {
180
- a: 'b'
181
- },
182
- {
183
- c: 'd'
184
- }
185
- ]
186
- },
187
- params: {
188
- section: 'mock-section',
189
- question: 'mock-question'
190
- }
191
- }),
192
- expected: (req, res, next) => {
193
- assert.deepStrictEqual(req.body.files, {
194
- 'an-existing-key-value-pair': [
195
- {
196
- rgb: 'xyz'
197
- }
198
- ],
199
- [filesPropertyPath]: [
200
- {
201
- a: 'b'
202
- },
203
- {
204
- c: 'd'
205
- }
206
- ]
207
- });
208
- assert.strictEqual(next.mock.callCount(), 1);
209
- }
210
- }
211
- ].forEach(({ description, given, expected }) => {
212
- it(description, () => {
213
- const next = mock.fn();
214
- const req = given();
215
-
216
- dynamicReqFilesToReqBodyFilesMiddleware()(req, mockRes(), next);
217
-
218
- expected(req, mockRes(), next);
219
- });
220
- });
221
- });
@@ -1,38 +0,0 @@
1
- import BaseValidator from './base-validator.js';
2
- import { checkSchema } from 'express-validator';
3
-
4
- import requiredFileUploadSchema from './schemas/required-file-upload-schema.js';
5
-
6
- /**
7
- * @typedef {import('../questions/question.js')} Question
8
- */
9
-
10
- /**
11
- * enforces that at least one file is present for a question, whether it is being/already uploaded
12
- * @class
13
- */
14
- export default class RequiredFileUploadValidator extends BaseValidator {
15
- /**
16
- * creates an instance of a RequiredFileUploadValidator
17
- * @param {string} [errorMessage] - custom error message to show on validation failure
18
- */
19
- constructor(errorMessage) {
20
- super();
21
-
22
- if (errorMessage) {
23
- this.errorMessage = errorMessage;
24
- } else {
25
- this.errorMessage = 'You must add your documents';
26
- }
27
- }
28
-
29
- /**
30
- * validates against path based on questionObj's fieldname
31
- * @param {Question} questionObj
32
- */
33
- validate(questionObj, journeyResponse) {
34
- const path = questionObj.fieldName;
35
- const documentType = questionObj.documentType.name;
36
- return checkSchema(requiredFileUploadSchema(path, journeyResponse, documentType, this.errorMessage))[0];
37
- }
38
- }
@@ -1,141 +0,0 @@
1
- import { describe, it } from 'node:test';
2
- import assert from 'node:assert';
3
- import RequiredFileUploadValidator from './required-file-upload-validator.js';
4
-
5
- describe('./src/dynamic-forms/validator/required-file-upload-validator.js', () => {
6
- describe('validate', () => {
7
- it('should have a rule for path featuring fieldName', () => {
8
- const requiredFileUploadValidator = new RequiredFileUploadValidator();
9
-
10
- const rule = requiredFileUploadValidator
11
- .validate({
12
- fieldName: 'test-field-name',
13
- documentType: {
14
- name: 'test-doc-type'
15
- }
16
- })
17
- .builder.build();
18
-
19
- assert.deepStrictEqual(rule.fields, ['test-field-name']);
20
- assert.strictEqual(!!rule.optional, false);
21
- assert.strictEqual(rule.stack.length, 1);
22
- assert.strictEqual(rule.stack[0].validator.name, 'options');
23
- assert.strictEqual(!!rule.stack[0].validator.negated, false);
24
- });
25
-
26
- it('should pass if file being uploaded', async () => {
27
- const questionObj = {
28
- fieldName: 'test-fieldname',
29
- documentType: {
30
- name: 'test-doc-type'
31
- }
32
- };
33
- const journeyResponse = {};
34
- const req = { files: { test: 'test123' } };
35
-
36
- const requiredFileUploadValidator = new RequiredFileUploadValidator();
37
- const validationResult = await requiredFileUploadValidator.validate(questionObj, journeyResponse).run(req);
38
-
39
- assert.strictEqual(validationResult.errors.length, 0);
40
- });
41
-
42
- it('should pass if no file being uploaded and file matching question document type already uploaded', async () => {
43
- const questionObj = {
44
- fieldName: 'another-test-fieldname',
45
- documentType: {
46
- name: 'test-doc-type'
47
- }
48
- };
49
- const uploadedFile = {
50
- file: 'already-uploaded',
51
- type: 'test-doc-type'
52
- };
53
- const journeyResponse = {
54
- answers: { SubmissionDocumentUpload: [uploadedFile] }
55
- };
56
- const req = { body: {} };
57
-
58
- const requiredFileUploadValidator = new RequiredFileUploadValidator();
59
- const validationResult = await requiredFileUploadValidator.validate(questionObj, journeyResponse).run(req);
60
-
61
- assert.strictEqual(validationResult.errors.length, 0);
62
- });
63
-
64
- it('should fail if no file being uploaded and no file matching question document type already uploaded', async () => {
65
- const questionObj = {
66
- fieldName: 'yet-another-test-fieldname',
67
- documentType: {
68
- name: 'test-doc-type'
69
- }
70
- };
71
- const journeyResponse = { answers: { SubmissionDocumentUpload: [] } };
72
- const req = { body: {} };
73
-
74
- const requiredFileUploadValidator = new RequiredFileUploadValidator();
75
- const validationResult = await requiredFileUploadValidator.validate(questionObj, journeyResponse).run(req);
76
-
77
- assert.strictEqual(validationResult.errors.length, 1);
78
- });
79
-
80
- it('should fail if no file being uploaded matching question doc type and all existing files matching doc type being removed', async () => {
81
- const questionObj = {
82
- fieldName: 'test-fieldname',
83
- documentType: {
84
- name: 'test-doc-type'
85
- }
86
- };
87
-
88
- const journeyResponse = {
89
- answers: {
90
- SubmissionDocumentUpload: [
91
- {
92
- file: 'to-be-removed',
93
- type: 'test-doc-type'
94
- },
95
- {
96
- file: 'to-be-removed-too',
97
- type: 'test-doc-type'
98
- }
99
- ]
100
- }
101
- };
102
- const req = { body: {} };
103
- req.body.removedFiles =
104
- '[{"file": "to-be-removed", "type": "test-doc-type"}, {"file": "to-be-removed-too", "type": "test-doc-type"}]';
105
- const requiredFileUploadValidator = new RequiredFileUploadValidator();
106
- const validationResult = await requiredFileUploadValidator.validate(questionObj, journeyResponse).run(req);
107
-
108
- assert.strictEqual(validationResult.errors.length, 1);
109
- });
110
-
111
- it('should pass if existing files matching question doc type and only some being removed', async () => {
112
- const questionObj = {
113
- fieldName: 'test-fieldname',
114
- documentType: {
115
- name: 'test-doc-type'
116
- }
117
- };
118
- const journeyResponse = {
119
- answers: {
120
- SubmissionDocumentUpload: [
121
- {
122
- file: 'to-be-removed',
123
- type: 'test-doc-type'
124
- },
125
- {
126
- file: 'to-be-kept',
127
- type: 'test-doc-type'
128
- }
129
- ]
130
- }
131
- };
132
- const req = { body: {} };
133
- req.body.removedFiles = '[{"file": "to-be-removed", "type": "test-doc-type"}]';
134
-
135
- const requiredFileUploadValidator = new RequiredFileUploadValidator();
136
- const validationResult = await requiredFileUploadValidator.validate(questionObj, journeyResponse).run(req);
137
-
138
- assert.strictEqual(validationResult.errors.length, 0);
139
- });
140
- });
141
- });