From a892a3c063961dcf632442c0dceff4f6f75651b1 Mon Sep 17 00:00:00 2001 From: Mohammed Khalid Date: Tue, 16 Dec 2025 15:24:54 +0000 Subject: [PATCH] feat: display sections with forms that use sectionId --- package-lock.json | 14 ++-- package.json | 2 +- .../plugins/engine/models/FormModel.test.ts | 70 +++++++++++++++++++ src/server/plugins/engine/models/FormModel.ts | 9 ++- .../engine/pageControllers/PageController.ts | 6 +- .../QuestionPageController.test.ts | 12 ++-- test/form/definitions/conditions-basic.js | 6 +- 7 files changed, 100 insertions(+), 19 deletions(-) diff --git a/package-lock.json b/package-lock.json index 21145ba4a..f7a83d675 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "hasInstallScript": true, "license": "SEE LICENSE IN LICENSE", "dependencies": { - "@defra/forms-model": "^3.0.585", + "@defra/forms-model": "^3.0.597", "@defra/hapi-tracing": "^1.29.0", "@elastic/ecs-pino-format": "^1.5.0", "@hapi/boom": "^10.0.1", @@ -136,7 +136,7 @@ }, "engines": { "node": ">=22.11.0 <25.0.0", - "npm": "^10.9.0" + "npm": ">=10.9.0 <11.6.4" } }, "node_modules/@adobe/css-tools": { @@ -2218,9 +2218,9 @@ } }, "node_modules/@defra/forms-model": { - "version": "3.0.585", - "resolved": "https://registry.npmjs.org/@defra/forms-model/-/forms-model-3.0.585.tgz", - "integrity": "sha512-lSJzQu0xTk+VqSjLcjt7zwPS88J7MVYl5kdKfKCQsqbnVTmBHi9a3MHvCa6xlZRu1GZgoEjwQWY+QKzTKfN8FA==", + "version": "3.0.597", + "resolved": "https://registry.npmjs.org/@defra/forms-model/-/forms-model-3.0.597.tgz", + "integrity": "sha512-msdGKxl4L3GxPeF4dAFyTOwGgNExAeQWh5SkuzESdzRa9IQhlDzNh6fniT7D02b6vjGbHoy8jMQ5E/gjuvyBCw==", "license": "OGL-UK-3.0", "dependencies": { "@joi/date": "^2.1.1", @@ -2230,8 +2230,8 @@ "uuid": "^11.1.0" }, "engines": { - "node": "^22.11.0", - "npm": "^10.9.0" + "node": "^22.12.0", + "npm": ">=10.9.0 <11.6.4" }, "peerDependencies": { "joi": "^17.0.0" diff --git a/package.json b/package.json index 4750dc2df..438aef924 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,7 @@ }, "license": "SEE LICENSE IN LICENSE", "dependencies": { - "@defra/forms-model": "^3.0.585", + "@defra/forms-model": "^3.0.597", "@defra/hapi-tracing": "^1.29.0", "@elastic/ecs-pino-format": "^1.5.0", "@hapi/boom": "^10.0.1", diff --git a/src/server/plugins/engine/models/FormModel.test.ts b/src/server/plugins/engine/models/FormModel.test.ts index cd77cfcec..1e162d7df 100644 --- a/src/server/plugins/engine/models/FormModel.test.ts +++ b/src/server/plugins/engine/models/FormModel.test.ts @@ -716,4 +716,74 @@ describe('FormModel - Joined Conditions', () => { expect(Object.keys(model.conditions)).toHaveLength(3) }) }) + + describe('getSection', () => { + it('should look up section by name for V1 schema', () => { + const v1Definition = { + ...definition, + sections: [ + { name: 'personal', title: 'Personal details' }, + { name: 'contact', title: 'Contact details' } + ] + } + + const model = new FormModel(v1Definition, { basePath: 'test' }) + + expect(model.getSection('personal')).toEqual( + expect.objectContaining({ + name: 'personal', + title: 'Personal details' + }) + ) + expect(model.getSection('contact')).toEqual( + expect.objectContaining({ + name: 'contact', + title: 'Contact details' + }) + ) + expect(model.getSection('nonexistent')).toBeUndefined() + }) + + it('should look up section by ID for V2 schema', () => { + const v2Definition = { + ...definitionV2, + sections: [ + { + id: 'a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d', + name: 'personal', + title: 'Personal details' + }, + { + id: 'b2c3d4e5-6f7a-8b9c-0d1e-2f3a4b5c6d7e', + name: 'contact', + title: 'Contact details' + } + ] + } + + formDefinitionV2Schema.validate = jest + .fn() + .mockReturnValue({ value: v2Definition }) + + const model = new FormModel(v2Definition, { basePath: 'test' }) + + expect(model.getSection('a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d')).toEqual( + expect.objectContaining({ + id: 'a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d', + name: 'personal', + title: 'Personal details' + }) + ) + expect(model.getSection('b2c3d4e5-6f7a-8b9c-0d1e-2f3a4b5c6d7e')).toEqual( + expect.objectContaining({ + id: 'b2c3d4e5-6f7a-8b9c-0d1e-2f3a4b5c6d7e', + name: 'contact', + title: 'Contact details' + }) + ) + // V2 should not find by name + expect(model.getSection('personal')).toBeUndefined() + expect(model.getSection('nonexistent')).toBeUndefined() + }) + }) }) diff --git a/src/server/plugins/engine/models/FormModel.ts b/src/server/plugins/engine/models/FormModel.ts index 997c35c28..c54b1929b 100644 --- a/src/server/plugins/engine/models/FormModel.ts +++ b/src/server/plugins/engine/models/FormModel.ts @@ -21,7 +21,8 @@ import { type Engine, type FormDefinition, type List, - type Page + type Page, + type Section } from '@defra/forms-model' import { add, format } from 'date-fns' import { Parser, type Value } from 'expr-eval-fork' @@ -321,6 +322,12 @@ export class FormModel { : this.lists.find((list) => list.id === nameOrId) } + getSection(nameOrId: string): Section | undefined { + return this.schemaVersion === SchemaVersion.V1 + ? this.sections.find((section) => section.name === nameOrId) + : this.sections.find((section) => section.id === nameOrId) + } + /** * Form context for the current page */ diff --git a/src/server/plugins/engine/pageControllers/PageController.ts b/src/server/plugins/engine/pageControllers/PageController.ts index fef76c6ec..3254ff312 100644 --- a/src/server/plugins/engine/pageControllers/PageController.ts +++ b/src/server/plugins/engine/pageControllers/PageController.ts @@ -55,9 +55,9 @@ export class PageController { this.events = pageDef.events // Resolve section - this.section = model.sections.find( - (section) => section.name === pageDef.section - ) + if (pageDef.section) { + this.section = model.getSection(pageDef.section) + } // Resolve condition if (pageDef.condition) { diff --git a/src/server/plugins/engine/pageControllers/QuestionPageController.test.ts b/src/server/plugins/engine/pageControllers/QuestionPageController.test.ts index f7098bd1d..f42fe847d 100644 --- a/src/server/plugins/engine/pageControllers/QuestionPageController.test.ts +++ b/src/server/plugins/engine/pageControllers/QuestionPageController.test.ts @@ -1007,11 +1007,13 @@ describe('QuestionPageController V2', () => { it('returns the page section', () => { expect(controller1).toHaveProperty('section', undefined) - expect(controller2).toHaveProperty('section', { - name: 'marriage', - title: 'Your marriage', - hideTitle: false - }) + expect(controller2.section).toEqual( + expect.objectContaining({ + name: 'marriage', + title: 'Your marriage', + hideTitle: false + }) + ) }) }) diff --git a/test/form/definitions/conditions-basic.js b/test/form/definitions/conditions-basic.js index dc0581e4d..579654845 100644 --- a/test/form/definitions/conditions-basic.js +++ b/test/form/definitions/conditions-basic.js @@ -142,7 +142,7 @@ export const V2 = /** @satisfies {FormDefinition} */ ({ } ], condition: '6c9e2f4a-1d7b-5e8c-3f6a-9e2d5b7c4f1a', - section: 'marriage', + section: 'a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d', next: [] }, { @@ -154,8 +154,10 @@ export const V2 = /** @satisfies {FormDefinition} */ ({ lists: [], sections: [ { + id: 'a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d', name: 'marriage', - title: 'Your marriage' + title: 'Your marriage', + hideTitle: false } ], conditions: [