From a51875c3f1292da5f07c54603a746941e1963ea2 Mon Sep 17 00:00:00 2001 From: Denis Lugowski Date: Mon, 2 Mar 2026 15:13:11 +0100 Subject: [PATCH] feat(frontend): Add useSectionSpawning test --- .../composables/useSectionSpawning.spec.ts | 464 ++++++++++++++++++ 1 file changed, 464 insertions(+) create mode 100644 legalconsenthub/test/unit/composables/useSectionSpawning.spec.ts diff --git a/legalconsenthub/test/unit/composables/useSectionSpawning.spec.ts b/legalconsenthub/test/unit/composables/useSectionSpawning.spec.ts new file mode 100644 index 0000000..528950d --- /dev/null +++ b/legalconsenthub/test/unit/composables/useSectionSpawning.spec.ts @@ -0,0 +1,464 @@ +import { describe, it, expect } from 'vitest' +import { useSectionSpawning } from '../../../app/composables/useSectionSpawning' +import type { + FormElementSectionDto, + FormElementSubSectionDto, + FormElementDto, + FormOptionDto, + SectionSpawnTriggerDto, + FormElementType +} from '../../../.api-client' + +// Helper to create a FormOptionDto +function createOption(value: string, label: string): FormOptionDto { + return { + value, + label, + processingPurpose: 'NONE', + employeeDataCategory: 'NONE' + } +} + +// Helper to create a FormElementDto +function createFormElement( + reference: string, + title: string, + type: FormElementType, + options: FormOptionDto[] = [], + sectionSpawnTriggers: SectionSpawnTriggerDto[] = [] +): FormElementDto { + return { + id: `id-${reference}`, + reference, + title, + type, + options, + sectionSpawnTriggers + } +} + +// Helper to create a SectionSpawnTriggerDto +function createSpawnTrigger( + templateReference: string, + conditionType: 'SHOW' | 'HIDE' = 'SHOW', + expectedValue: string = 'true', + operator: 'EQUALS' | 'NOT_EQUALS' | 'IS_EMPTY' | 'IS_NOT_EMPTY' | 'CONTAINS' | 'NOT_CONTAINS' = 'EQUALS' +): SectionSpawnTriggerDto { + return { + templateReference, + sectionSpawnConditionType: conditionType, + sectionSpawnExpectedValue: expectedValue, + sectionSpawnOperator: operator + } +} + +// Helper to create a template section with title interpolation +function createTemplateSection( + templateReference: string, + title: string, + titleTemplate?: string, + shortTitle?: string, + description?: string, + formElementSubSections: FormElementSubSectionDto[] = [] +): FormElementSectionDto { + return { + id: `template-${templateReference}`, + title, + shortTitle, + description, + titleTemplate, + isTemplate: true, + templateReference, + formElementSubSections + } +} + +describe('useSectionSpawning', () => { + it('should remove spawned section when condition no longer met', () => { + const { processSpawnTriggers } = useSectionSpawning() + + const templateSection = createTemplateSection('template-1', 'Template Section') + const spawnedSection: FormElementSectionDto = { + id: 'spawned-1', + title: 'Spawned Section', + isTemplate: false, + spawnedFromElementReference: 'trigger_1', + templateReference: 'template-1', + formElementSubSections: [] + } + + const trigger = createSpawnTrigger('template-1', 'SHOW', 'true', 'EQUALS') + const triggerElement = createFormElement( + 'trigger_1', + 'Trigger', + 'SELECT', + [createOption('true', 'No'), createOption('false', 'Yes')], + [trigger] + ) + + const sections = [templateSection, spawnedSection] + const result = processSpawnTriggers(sections, [triggerElement]) + + expect(result).toHaveLength(1) + expect(result[0]!.isTemplate).toBe(true) + }) + + it('should NOT remove spawned section if another trigger for same template still satisfies condition', () => { + const { processSpawnTriggers } = useSectionSpawning() + + const templateSection = createTemplateSection('template-1', 'Template Section') + const spawnedSection: FormElementSectionDto = { + id: 'spawned-1', + title: 'Spawned Section', + isTemplate: false, + spawnedFromElementReference: 'trigger_1', + templateReference: 'template-1', + formElementSubSections: [] + } + + const trigger1 = createSpawnTrigger('template-1', 'SHOW', 'true', 'EQUALS') + const trigger2 = createSpawnTrigger('template-1', 'SHOW', 'maybe', 'EQUALS') + const triggerElement = createFormElement( + 'trigger_1', + 'Trigger', + 'SELECT', + [createOption('false', 'No'), createOption('false', 'Yes'), createOption('true', 'Maybe')], + [trigger1, trigger2] + ) + + const sections = [templateSection, spawnedSection] + const result = processSpawnTriggers(sections, [triggerElement]) + + expect(result).toHaveLength(2) + expect(result[1]!.spawnedFromElementReference).toBe('trigger_1') + }) + + it('should update titles with {{triggerValue}} interpolation when value changes', () => { + const { processSpawnTriggers } = useSectionSpawning() + + const templateSection = createTemplateSection( + 'template-1', + 'Section title', + 'Section template title {{triggerValue}}', + 'Short title {{triggerValue}}', + 'Description {{triggerValue}}' + ) + const spawnedSection: FormElementSectionDto = { + id: 'spawned-1', + title: 'Section OldValue', + shortTitle: 'Short OldValue', + description: 'Description OldValue', + isTemplate: false, + spawnedFromElementReference: 'trigger_1', + templateReference: 'template-1', + formElementSubSections: [] + } + + const trigger = createSpawnTrigger('template-1', 'SHOW', 'yes', 'EQUALS') + const triggerElement = createFormElement( + 'trigger_1', + 'Trigger', + 'SELECT', + [createOption('false', 'No'), createOption('true', 'Yes')], + [trigger] + ) + + const sections = [templateSection, spawnedSection] + const result = processSpawnTriggers(sections, [triggerElement]) + + expect(result[1]!.title).toBe('Section template title Yes') + expect(result[1]!.shortTitle).toBe('Short title Yes') + expect(result[1]!.description).toBe('Description Yes') + }) + + it('should insert spawned section after template section', () => { + const { processSpawnTriggers } = useSectionSpawning() + + const templateSection = createTemplateSection('template-1', 'Template Section') + const otherSection: FormElementSectionDto = { + id: 'other-1', + title: 'Other Section', + isTemplate: false, + formElementSubSections: [] + } + + const trigger = createSpawnTrigger('template-1', 'SHOW', 'Yes', 'EQUALS') + const triggerElement = createFormElement( + 'trigger_1', + 'Trigger', + 'SELECT', + [createOption('false', 'No'), createOption('true', 'Yes')], + [trigger] + ) + + const sections = [templateSection, otherSection] + const result = processSpawnTriggers(sections, [triggerElement]) + + expect(result).toHaveLength(3) + expect(result[0]!.isTemplate).toBe(true) + expect(result[1]!.spawnedFromElementReference).toBe('trigger_1') + expect(result[2]!.id).toBe('other-1') + }) + + it('should handle TEXTFIELD element value extraction', () => { + const { processSpawnTriggers } = useSectionSpawning() + + const templateSection = createTemplateSection('template-1', 'Section {{triggerValue}}', 'Section {{triggerValue}}') + const trigger = createSpawnTrigger('template-1', 'SHOW', 'john', 'EQUALS') + const triggerElement = createFormElement('trigger_1', 'Name', 'TEXTFIELD', [createOption('john', '')], [trigger]) + + const sections = [templateSection] + const result = processSpawnTriggers(sections, [triggerElement]) + + expect(result).toHaveLength(2) + expect(result[1]!.title).toBe('Section john') + }) + + it('should interpolate shortTitle with {{triggerValue}}', () => { + const { processSpawnTriggers } = useSectionSpawning() + + const templateSection = createTemplateSection('template-1', 'Template', undefined, 'Short {{triggerValue}}') + const spawnedSection: FormElementSectionDto = { + id: 'spawned-1', + title: 'Spawned', + shortTitle: 'Short OldValue', + isTemplate: false, + spawnedFromElementReference: 'trigger_1', + templateReference: 'template-1', + formElementSubSections: [] + } + + const trigger = createSpawnTrigger('template-1', 'SHOW', 'yes', 'EQUALS') + const triggerElement = createFormElement( + 'trigger_1', + 'Trigger', + 'SELECT', + [createOption('false', 'No'), createOption('true', 'Yes')], + [trigger] + ) + + const sections = [templateSection, spawnedSection] + const result = processSpawnTriggers(sections, [triggerElement]) + + expect(result[1]!.shortTitle).toBe('Short Yes') + }) + + it('should interpolate description with {{triggerValue}}', () => { + const { processSpawnTriggers } = useSectionSpawning() + + const templateSection = createTemplateSection( + 'template-1', + 'Template', + undefined, + undefined, + 'Description {{triggerValue}}' + ) + const spawnedSection: FormElementSectionDto = { + id: 'spawned-1', + title: 'Spawned', + description: 'Description OldValue', + isTemplate: false, + spawnedFromElementReference: 'trigger_1', + templateReference: 'template-1', + formElementSubSections: [] + } + + const trigger = createSpawnTrigger('template-1', 'SHOW', 'yes', 'EQUALS') + const triggerElement = createFormElement( + 'trigger_1', + 'Trigger', + 'SELECT', + [createOption('false', 'No'), createOption('true', 'Yes')], + [trigger] + ) + + const sections = [templateSection, spawnedSection] + const result = processSpawnTriggers(sections, [triggerElement]) + + expect(result[1]!.description).toBe('Description Yes') + }) + + it('should preserve non-triggering elements during processing', () => { + const { processSpawnTriggers } = useSectionSpawning() + + const templateSection = createTemplateSection('template-1', 'Template') + const nonTriggerElement = createFormElement('other_1', 'Other', 'TEXTFIELD', [createOption('value', '')]) + + const trigger = createSpawnTrigger('template-1', 'SHOW', 'Yes', 'EQUALS') + const triggerElement = createFormElement( + 'trigger_1', + 'Trigger', + 'SELECT', + [createOption('false', 'No'), createOption('true', 'Yes')], + [trigger] + ) + + const sections = [templateSection] + const result = processSpawnTriggers(sections, [triggerElement, nonTriggerElement]) + + expect(result).toHaveLength(2) + expect(result[1]!.spawnedFromElementReference).toBe('trigger_1') + }) + + it('should handle empty trigger list on element', () => { + const { processSpawnTriggers } = useSectionSpawning() + + const templateSection = createTemplateSection('template-1', 'Template') + const triggerElement = createFormElement( + 'trigger_1', + 'Trigger', + 'SELECT', + [createOption('false', 'No'), createOption('true', 'Yes')], + [] + ) + + const sections = [templateSection] + const result = processSpawnTriggers(sections, [triggerElement]) + + expect(result).toHaveLength(1) + }) + + describe('operators', () => { + describe('EQUALS', () => { + it('should spawn section when SHOW/EQUALS condition is met with SELECT element', () => { + const { processSpawnTriggers } = useSectionSpawning() + + const templateSection = createTemplateSection('template-1', 'Template Section', 'Section Title Template') + const trigger = createSpawnTrigger('template-1', 'SHOW', 'Yes', 'EQUALS') + const triggerElement = createFormElement( + 'trigger_1', + 'Trigger', + 'SELECT', + [createOption('false', 'No'), createOption('true', 'Yes')], + [trigger] + ) + + const result = processSpawnTriggers([templateSection], [triggerElement]) + + expect(result).toHaveLength(2) + expect(result[1]!.spawnedFromElementReference).toBe('trigger_1') + expect(result[1]!.templateReference).toBe('template-1') + expect(result[1]!.title).toBe('Section Title Template') + }) + + it('should NOT spawn section when SHOW/EQUALS condition is NOT met', () => { + const { processSpawnTriggers } = useSectionSpawning() + + const templateSection = createTemplateSection('template-1', 'Template Section') + const trigger = createSpawnTrigger('template-1', 'SHOW', 'Yes', 'EQUALS') + const triggerElement = createFormElement( + 'trigger_1', + 'Trigger', + 'SELECT', + [createOption('true', 'No'), createOption('false', 'Yes')], + [trigger] + ) + + const sections = [templateSection] + const result = processSpawnTriggers(sections, [triggerElement]) + + expect(result).toHaveLength(1) + }) + + it('should handle case-insensitive comparison for EQUALS operator', () => { + const { processSpawnTriggers } = useSectionSpawning() + + const templateSection = createTemplateSection('template-1', 'Template') + const trigger = createSpawnTrigger('template-1', 'SHOW', 'YES', 'EQUALS') + const triggerElement = createFormElement( + 'trigger_1', + 'Trigger', + 'SELECT', + [createOption('false', 'No'), createOption('true', 'yes')], + [trigger] + ) + + const sections = [templateSection] + const result = processSpawnTriggers(sections, [triggerElement]) + + expect(result).toHaveLength(2) + }) + }) + + describe('NOT_EQUALS', () => { + it('should handle NOT_EQUALS operator', () => { + const { processSpawnTriggers } = useSectionSpawning() + + const templateSection = createTemplateSection('template-1', 'Template') + const trigger = createSpawnTrigger('template-1', 'SHOW', 'Yes', 'NOT_EQUALS') + const triggerElement = createFormElement( + 'trigger_1', + 'Trigger', + 'SELECT', + [createOption('true', 'No'), createOption('false', 'Yes')], + [trigger] + ) + + const sections = [templateSection] + const result = processSpawnTriggers(sections, [triggerElement]) + + expect(result).toHaveLength(2) + expect(result[1]!.spawnedFromElementReference).toBe('trigger_1') + }) + }) + + describe('IS_EMPTY', () => { + it('should handle IS_EMPTY operator', () => { + const { processSpawnTriggers } = useSectionSpawning() + + const templateSection = createTemplateSection('template-1', 'Template Section') + const trigger = createSpawnTrigger('template-1', 'SHOW', 'PLACEHOLDER', 'IS_EMPTY') + const triggerElement = createFormElement('trigger_1', 'Trigger', 'TEXTFIELD', [createOption('', '')], [trigger]) + + const sections = [templateSection] + const result = processSpawnTriggers(sections, [triggerElement]) + + expect(result).toHaveLength(2) + expect(result[1]!.spawnedFromElementReference).toBe('trigger_1') + }) + }) + + describe('IS_NOT_EMPTY', () => { + it('should handle IS_NOT_EMPTY operator', () => { + const { processSpawnTriggers } = useSectionSpawning() + + const templateSection = createTemplateSection('template-1', 'Template Section') + const trigger = createSpawnTrigger('template-1', 'SHOW', 'PLACEHOLDER', 'IS_NOT_EMPTY') + const triggerElement = createFormElement( + 'trigger_1', + 'Trigger', + 'TEXTFIELD', + [createOption('some value', '')], + [trigger] + ) + + const sections = [templateSection] + const result = processSpawnTriggers(sections, [triggerElement]) + + expect(result).toHaveLength(2) + expect(result[1]!.spawnedFromElementReference).toBe('trigger_1') + }) + }) + + describe('HIDE', () => { + it('should handle HIDE condition type', () => { + const { processSpawnTriggers } = useSectionSpawning() + + const templateSection = createTemplateSection('template-1', 'Template Section') + const trigger = createSpawnTrigger('template-1', 'HIDE', 'No', 'EQUALS') + const triggerElement = createFormElement( + 'trigger_1', + 'Trigger', + 'SELECT', + [createOption('true', 'No'), createOption('false', 'Yes')], + [trigger] + ) + + const sections = [templateSection] + const result = processSpawnTriggers(sections, [triggerElement]) + + expect(result).toHaveLength(1) + }) + }) + }) +})