From 4b99ee541a86383d60cbbcfe9acf7d33226b7cf3 Mon Sep 17 00:00:00 2001 From: nsemets Date: Fri, 25 Sep 2026 17:02:54 +0300 Subject: [PATCH] fix(registration): updated logic for schema without page heading --- .../registration/page-schema.mapper.spec.ts | 156 ++++++++++++++++++ .../registration/page-schema.mapper.ts | 13 ++ 2 files changed, 169 insertions(+) create mode 100644 src/app/shared/mappers/registration/page-schema.mapper.spec.ts diff --git a/src/app/shared/mappers/registration/page-schema.mapper.spec.ts b/src/app/shared/mappers/registration/page-schema.mapper.spec.ts new file mode 100644 index 000000000..93ed01558 --- /dev/null +++ b/src/app/shared/mappers/registration/page-schema.mapper.spec.ts @@ -0,0 +1,156 @@ +import { BlockType } from '@osf/shared/enums/block-type.enum'; +import { FieldType } from '@osf/shared/enums/field-type.enum'; +import { SchemaBlocksResponseJsonApi } from '@osf/shared/models/registration/schema-blocks-json-api.model'; + +import { PageSchemaMapper } from './page-schema.mapper'; + +type SchemaBlock = SchemaBlocksResponseJsonApi['data'][number]; + +function block(id: string, blockType: BlockType, overrides: Partial = {}): SchemaBlock { + return { + id, + type: 'schema-blocks', + attributes: { + block_type: blockType, + display_text: '', + example_text: '', + help_text: '', + index: 0, + registration_response_key: null, + required: false, + schema_block_group_key: id, + ...overrides, + }, + }; +} + +function response(blocks: SchemaBlock[]): SchemaBlocksResponseJsonApi { + return { data: blocks, meta: { total: blocks.length } }; +} + +describe('PageSchemaMapper', () => { + it('should return no pages when the response has no blocks', () => { + expect(PageSchemaMapper.fromSchemaBlocksResponse(response([]))).toEqual([]); + }); + + it('should map a page heading and attach its question', () => { + const pages = PageSchemaMapper.fromSchemaBlocksResponse( + response([ + block('page-1', BlockType.PageHeading, { display_text: 'Study & design', help_text: 'Help' }), + block('q-1', BlockType.QuestionLabel, { + display_text: 'Research questions', + schema_block_group_key: 'g1', + }), + block('input-1', BlockType.LongTextInput, { + registration_response_key: 'q1', + required: true, + schema_block_group_key: 'g1', + }), + ]) + ); + + expect(pages).toEqual([ + { + id: 'page-1', + title: 'Study & design', + helpText: 'Help', + questions: [ + { + id: 'q-1', + displayText: 'Research questions', + helpText: '', + exampleText: '', + required: true, + groupKey: 'g1', + responseKey: 'q1', + fieldType: FieldType.TextArea, + }, + ], + }, + ]); + }); + + it('should attach section questions to the section', () => { + const pages = PageSchemaMapper.fromSchemaBlocksResponse( + response([ + block('page-1', BlockType.PageHeading, { display_text: 'Methods' }), + block('section-1', BlockType.SectionHeading, { display_text: 'Analysis' }), + block('q-1', BlockType.QuestionLabel, { display_text: 'Data processing', schema_block_group_key: 'g1' }), + block('input-1', BlockType.ShortTextInput, { + registration_response_key: 'q2', + required: true, + schema_block_group_key: 'g1', + }), + ]) + ); + + expect(pages[0].questions).toEqual([]); + expect(pages[0].sections).toEqual([ + { + id: 'section-1', + title: 'Analysis', + helpText: '', + questions: [ + { + id: 'q-1', + displayText: 'Data processing', + helpText: '', + exampleText: '', + required: true, + groupKey: 'g1', + responseKey: 'q2', + fieldType: FieldType.Text, + }, + ], + }, + ]); + }); + + it('should wrap questions without a page heading into one page', () => { + const pages = PageSchemaMapper.fromSchemaBlocksResponse( + response([ + block('q-1', BlockType.QuestionLabel, { display_text: 'Research questions', schema_block_group_key: 'g1' }), + block('input-1', BlockType.LongTextInput, { + registration_response_key: 'q1', + required: true, + schema_block_group_key: 'g1', + }), + block('q-2', BlockType.QuestionLabel, { display_text: 'Upload files', schema_block_group_key: 'g2' }), + block('input-2', BlockType.FileInput, { + registration_response_key: '53-7', + required: false, + schema_block_group_key: 'g2', + }), + ]) + ); + + expect(pages).toEqual([ + { + id: 'q-1', + title: '', + questions: [ + { + id: 'q-1', + displayText: 'Research questions', + helpText: '', + exampleText: '', + required: true, + groupKey: 'g1', + responseKey: 'q1', + fieldType: FieldType.TextArea, + }, + { + id: 'q-2', + displayText: 'Upload files', + helpText: '', + exampleText: '', + required: false, + groupKey: 'g2', + responseKey: '53-7', + fieldType: FieldType.File, + }, + ], + }, + ]); + }); +}); diff --git a/src/app/shared/mappers/registration/page-schema.mapper.ts b/src/app/shared/mappers/registration/page-schema.mapper.ts index c42851d43..dc3f2b474 100644 --- a/src/app/shared/mappers/registration/page-schema.mapper.ts +++ b/src/app/shared/mappers/registration/page-schema.mapper.ts @@ -7,6 +7,7 @@ import { SchemaBlocksResponseJsonApi } from '@shared/models/registration/schema- export class PageSchemaMapper { static fromSchemaBlocksResponse(response: SchemaBlocksResponseJsonApi): PageSchema[] { const pages: PageSchema[] = []; + const unpagedQuestions: Question[] = []; let currentPage!: PageSchema; let currentQuestion: Question | null = null; let currentSection: Section | null = null; @@ -75,6 +76,8 @@ export class PageSchemaMapper { } else if (currentPage) { currentPage.questions = currentPage.questions || []; currentPage.questions.push(currentQuestion); + } else { + unpagedQuestions.push(currentQuestion); } break; @@ -94,6 +97,8 @@ export class PageSchemaMapper { } else if (currentPage) { currentPage.questions = currentPage.questions || []; currentPage.questions.push(currentQuestion); + } else { + unpagedQuestions.push(currentQuestion); } break; @@ -151,6 +156,14 @@ export class PageSchemaMapper { } }); + if (!pages.length && unpagedQuestions.length) { + pages.push({ + id: unpagedQuestions[0].id, + title: '', + questions: unpagedQuestions, + }); + } + return pages; } }