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) }) }) }) })