Files
crewli/apps/portal/tests/unit/formatFieldValue.spec.ts
bert.hausmans dda60ed5e4 refactor(form-schema): extract schema types and schema-driven behaviors to shared package
Moves formBuilder types, formValidation, useConditionalLogic, useFormSteps,
and formatFieldValue from apps/portal/src to packages/form-schema/src.
Adds @form-schema path alias to both apps/portal and apps/app.
Vue field components remain per-app to allow independent visual evolution.
Behavior-neutral: all 35 Vitest tests green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 23:57:39 +02:00

189 lines
6.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { describe, expect, it } from 'vitest'
import { formatFieldValue } from '@form-schema/composables/formatFieldValue'
import { FormFieldType } from '@form-schema/types/formBuilder'
import type {
AvailableTag,
PublicFormField,
PublicFormSectionOption,
PublicFormTimeSlot,
} from '@form-schema/types/formBuilder'
function field(partial: Partial<PublicFormField> = {}): PublicFormField {
return {
id: partial.id ?? 'f',
slug: partial.slug ?? 'slug',
field_type: partial.field_type ?? FormFieldType.TEXT,
label: partial.label ?? 'Label',
help_text: partial.help_text ?? null,
options: partial.options ?? null,
available_tags: partial.available_tags ?? null,
validation_rules: partial.validation_rules ?? null,
is_required: partial.is_required ?? false,
display_width: partial.display_width ?? 'full',
conditional_logic: partial.conditional_logic ?? null,
sort_order: partial.sort_order ?? 0,
form_schema_section_id: partial.form_schema_section_id ?? null,
}
}
function tag(partial: Partial<AvailableTag>): AvailableTag {
return { id: partial.id ?? 't_1', name: partial.name ?? 'Tag', category: partial.category ?? '' }
}
function timeSlot(partial: Partial<PublicFormTimeSlot>): PublicFormTimeSlot {
return {
id: partial.id ?? 'ts_1',
name: partial.name ?? 'Vrijdag avond',
date: partial.date ?? '2026-07-10',
start_time: partial.start_time ?? '18:00:00',
end_time: partial.end_time ?? '23:00:00',
duration_hours: partial.duration_hours ?? 5,
event_id: partial.event_id ?? 'evt_1',
event_name: partial.event_name ?? 'Event',
}
}
function section(partial: Partial<PublicFormSectionOption>): PublicFormSectionOption {
return {
id: partial.id ?? 's_1',
name: partial.name ?? 'Section',
category: partial.category ?? null,
icon: partial.icon ?? null,
registration_description: partial.registration_description ?? null,
}
}
describe('formatFieldValue', () => {
describe('empty values', () => {
it.each<[unknown]>([
[null],
[undefined],
[''],
[[]],
])('returns "—" for empty value %s on any field type', v => {
expect(formatFieldValue(field({ field_type: FormFieldType.TEXT }), v, [], [])).toBe('—')
})
})
describe('TAG_PICKER', () => {
it('maps IDs to tag names using field.available_tags', () => {
const f = field({
field_type: FormFieldType.TAG_PICKER,
available_tags: [tag({ id: 'a', name: 'Tapper' }), tag({ id: 'b', name: 'Barista' })],
})
expect(formatFieldValue(f, ['a', 'b'], undefined, undefined)).toBe('Tapper, Barista')
})
it('labels unknown tag IDs as "(onbekende tag)"', () => {
const f = field({
field_type: FormFieldType.TAG_PICKER,
available_tags: [tag({ id: 'a', name: 'Tapper' })],
})
expect(formatFieldValue(f, ['a', 'zzz'], undefined, undefined)).toBe('Tapper, (onbekende tag)')
})
})
describe('AVAILABILITY_PICKER', () => {
it('maps IDs to "name (startend)" with seconds stripped', () => {
const f = field({ field_type: FormFieldType.AVAILABILITY_PICKER })
const slots = [timeSlot({ id: 'x', name: 'Vrijwilligers afbraak zondag', start_time: '10:00:00', end_time: '16:00:00' })]
expect(formatFieldValue(f, ['x'], slots, undefined))
.toBe('Vrijwilligers afbraak zondag (10:0016:00)')
})
it('labels unknown time_slot IDs as "(onbekend tijdslot)"', () => {
const f = field({ field_type: FormFieldType.AVAILABILITY_PICKER })
const slots = [timeSlot({ id: 'x' })]
expect(formatFieldValue(f, ['x', 'y'], slots, undefined))
.toBe('Vrijdag avond (18:0023:00), (onbekend tijdslot)')
})
it('returns "Laden…" while the time-slots query is still fetching', () => {
const f = field({ field_type: FormFieldType.AVAILABILITY_PICKER })
expect(formatFieldValue(f, ['x'], undefined, undefined)).toBe('Laden…')
})
})
describe('SECTION_PRIORITY', () => {
it('renders "N. Name, …" sorted by priority', () => {
const f = field({ field_type: FormFieldType.SECTION_PRIORITY })
const sections = [
section({ id: 's_1', name: 'Hoofdpodium Bar' }),
section({ id: 's_2', name: 'Theatertent Bar' }),
]
expect(formatFieldValue(
f,
[{ section_id: 's_1', priority: 1 }, { section_id: 's_2', priority: 2 }],
undefined,
sections,
)).toBe('1. Hoofdpodium Bar, 2. Theatertent Bar')
})
it('re-sorts unordered input by priority before rendering', () => {
const f = field({ field_type: FormFieldType.SECTION_PRIORITY })
const sections = [section({ id: 's_1', name: 'A' }), section({ id: 's_2', name: 'B' })]
expect(formatFieldValue(
f,
[{ section_id: 's_2', priority: 2 }, { section_id: 's_1', priority: 1 }],
undefined,
sections,
)).toBe('1. A, 2. B')
})
it('labels unknown section IDs as "(onbekende sectie)"', () => {
const f = field({ field_type: FormFieldType.SECTION_PRIORITY })
const sections = [section({ id: 's_1', name: 'Known' })]
expect(formatFieldValue(
f,
[{ section_id: 's_1', priority: 1 }, { section_id: 'missing', priority: 2 }],
undefined,
sections,
)).toBe('1. Known, 2. (onbekende sectie)')
})
it('returns "—" when the value shape is malformed (defensive guard)', () => {
const f = field({ field_type: FormFieldType.SECTION_PRIORITY })
// Flat string[] — wrong shape, must not leak [object Object].
expect(formatFieldValue(f, ['s_1', 's_2'], undefined, [section({ id: 's_1', name: 'Known' })])).toBe('—')
})
it('returns "Laden…" while the sections query is still fetching', () => {
const f = field({ field_type: FormFieldType.SECTION_PRIORITY })
expect(formatFieldValue(
f,
[{ section_id: 's_1', priority: 1 }],
undefined,
undefined,
)).toBe('Laden…')
})
})
describe('scalars', () => {
it('formats BOOLEAN true as "Ja", false as "Nee"', () => {
const f = field({ field_type: FormFieldType.BOOLEAN })
expect(formatFieldValue(f, true, undefined, undefined)).toBe('Ja')
expect(formatFieldValue(f, false, undefined, undefined)).toBe('Nee')
})
it('stringifies unknown-type array values via join', () => {
const f = field({ field_type: FormFieldType.MULTISELECT })
expect(formatFieldValue(f, ['A', 'B'], undefined, undefined)).toBe('A, B')
})
it('stringifies scalars for text-like types', () => {
const f = field({ field_type: FormFieldType.TEXT })
expect(formatFieldValue(f, 'hello', undefined, undefined)).toBe('hello')
})
})
})