test(portal): add Vitest setup and public-form tests
Introduces vitest config, jsdom setup, and first suites covering FieldRenderer dispatch and useConditionalLogic evaluation. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
102
apps/portal/tests/components/public-form/FieldRenderer.test.ts
Normal file
102
apps/portal/tests/components/public-form/FieldRenderer.test.ts
Normal file
@@ -0,0 +1,102 @@
|
||||
import { mount } from '@vue/test-utils'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import FieldRenderer from '@/components/public-form/FieldRenderer.vue'
|
||||
import { FormFieldType } from '@/types/formBuilder'
|
||||
import type { PublicFormField } from '@/types/formBuilder'
|
||||
|
||||
function makeField(partial: Partial<PublicFormField>): PublicFormField {
|
||||
return {
|
||||
id: partial.id ?? 'id',
|
||||
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 mountRenderer(field: PublicFormField, allValues: Record<string, unknown> = {}) {
|
||||
return mount(FieldRenderer, {
|
||||
props: { field, modelValue: undefined, allValues },
|
||||
global: {
|
||||
stubs: {
|
||||
VCol: { name: 'VCol', template: '<div class="v-col-stub"><slot/></div>' },
|
||||
VAlert: { name: 'VAlert', template: '<div class="v-alert-stub"><slot/></div>' },
|
||||
FieldText: { name: 'FieldText', template: '<div class="field-text-stub"/>' },
|
||||
FieldTextarea: { name: 'FieldTextarea', template: '<div class="field-textarea-stub"/>' },
|
||||
FieldEmail: { name: 'FieldEmail', template: '<div class="field-email-stub"/>' },
|
||||
FieldPhone: { name: 'FieldPhone', template: '<div class="field-phone-stub"/>' },
|
||||
FieldNumber: { name: 'FieldNumber', template: '<div class="field-number-stub"/>' },
|
||||
FieldDate: { name: 'FieldDate', template: '<div class="field-date-stub"/>' },
|
||||
FieldBoolean: { name: 'FieldBoolean', template: '<div class="field-boolean-stub"/>' },
|
||||
FieldRadio: { name: 'FieldRadio', template: '<div class="field-radio-stub"/>' },
|
||||
FieldSelect: { name: 'FieldSelect', template: '<div class="field-select-stub"/>' },
|
||||
FieldMultiselect: { name: 'FieldMultiselect', template: '<div class="field-multiselect-stub"/>' },
|
||||
FieldCheckboxList: { name: 'FieldCheckboxList', template: '<div class="field-checkboxlist-stub"/>' },
|
||||
FieldHeading: { name: 'FieldHeading', template: '<div class="field-heading-stub"/>' },
|
||||
FieldParagraph: { name: 'FieldParagraph', template: '<div class="field-paragraph-stub"/>' },
|
||||
FieldUrl: { name: 'FieldUrl', template: '<div class="field-url-stub"/>' },
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
describe('FieldRenderer', () => {
|
||||
it.each<[string, string]>([
|
||||
[FormFieldType.TEXT, 'field-text-stub'],
|
||||
[FormFieldType.TEXTAREA, 'field-textarea-stub'],
|
||||
[FormFieldType.EMAIL, 'field-email-stub'],
|
||||
[FormFieldType.PHONE, 'field-phone-stub'],
|
||||
[FormFieldType.NUMBER, 'field-number-stub'],
|
||||
[FormFieldType.DATE, 'field-date-stub'],
|
||||
[FormFieldType.BOOLEAN, 'field-boolean-stub'],
|
||||
[FormFieldType.RADIO, 'field-radio-stub'],
|
||||
[FormFieldType.SELECT, 'field-select-stub'],
|
||||
[FormFieldType.MULTISELECT, 'field-multiselect-stub'],
|
||||
[FormFieldType.CHECKBOX_LIST, 'field-checkboxlist-stub'],
|
||||
[FormFieldType.HEADING, 'field-heading-stub'],
|
||||
[FormFieldType.PARAGRAPH, 'field-paragraph-stub'],
|
||||
[FormFieldType.URL, 'field-url-stub'],
|
||||
])('dispatches to the right component for %s', (fieldType, className) => {
|
||||
const wrapper = mountRenderer(makeField({ field_type: fieldType as typeof FormFieldType[keyof typeof FormFieldType] }))
|
||||
expect(wrapper.find(`.${className}`).exists()).toBe(true)
|
||||
})
|
||||
|
||||
it.each<string>([
|
||||
FormFieldType.TAG_PICKER,
|
||||
FormFieldType.AVAILABILITY_PICKER,
|
||||
FormFieldType.SECTION_PRIORITY,
|
||||
FormFieldType.FILE_UPLOAD,
|
||||
FormFieldType.IMAGE_UPLOAD,
|
||||
FormFieldType.SIGNATURE,
|
||||
FormFieldType.TABLE_ROWS,
|
||||
FormFieldType.DATETIME,
|
||||
])('renders placeholder alert for out-of-scope type %s', fieldType => {
|
||||
const wrapper = mountRenderer(makeField({ field_type: fieldType as typeof FormFieldType[keyof typeof FormFieldType] }))
|
||||
expect(wrapper.find('.v-alert-stub').exists()).toBe(true)
|
||||
expect(wrapper.text()).toContain('binnenkort ondersteund')
|
||||
})
|
||||
|
||||
it('hides the field when conditional logic evaluates to false', () => {
|
||||
const field = makeField({
|
||||
field_type: FormFieldType.TEXT,
|
||||
conditional_logic: {
|
||||
show_when: { all: [{ field_slug: 'gate', operator: 'equals', value: 'yes' }] },
|
||||
},
|
||||
})
|
||||
|
||||
const hidden = mountRenderer(field, { gate: 'no' })
|
||||
expect(hidden.find('.field-text-stub').exists()).toBe(false)
|
||||
expect(hidden.find('.v-col-stub').exists()).toBe(false)
|
||||
|
||||
const shown = mountRenderer(field, { gate: 'yes' })
|
||||
expect(shown.find('.field-text-stub').exists()).toBe(true)
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user