import { mount } from '@vue/test-utils'
import { describe, expect, it } from 'vitest'
import { computed, defineComponent, h, ref } from 'vue'
import FieldCheckboxList from '@/components/public-form/FieldCheckboxList.vue'
import FieldMultiselect from '@/components/public-form/FieldMultiselect.vue'
import FieldRadio from '@/components/public-form/FieldRadio.vue'
import FieldSelect from '@/components/public-form/FieldSelect.vue'
import { providePublicFormLocale } from '@/composables/publicFormInjection'
import { FormFieldType } from '@form-schema/types/formBuilder'
import type { OptionSpec, PublicFormField } from '@form-schema/types/formBuilder'
const stubs = {
VRadioGroup: { name: 'VRadioGroup', template: '
' },
VRadio: {
name: 'VRadio',
props: ['value', 'label'],
template: '',
},
VCheckbox: {
name: 'VCheckbox',
props: ['modelValue', 'label'],
template: '',
},
AppSelect: {
name: 'AppSelect',
props: ['modelValue', 'items', 'itemTitle', 'itemValue', 'label'],
template: `
{{ item.title }}
`,
},
}
function fieldOf(field_type: PublicFormField['field_type'], options: OptionSpec[]): PublicFormField {
return {
id: 'f_1',
slug: 'choice',
field_type,
label: 'Choice',
help_text: null,
options,
available_tags: null,
validation_rules: null,
is_required: false,
display_width: 'full',
conditional_logic: null,
sort_order: 1,
form_schema_section_id: null,
}
}
function harness(component: any, field: PublicFormField, locale: string) {
// Tiny wrapper that calls providePublicFormLocale before rendering the
// target component, mimicking what [public_token].vue does at the page
// root.
const Wrapper = defineComponent({
setup() {
providePublicFormLocale(ref(locale))
return () => h(component, { field, modelValue: null })
},
})
return mount(Wrapper, { global: { stubs } })
}
const optionsSample: OptionSpec[] = [
{ value: 'red', label: 'Red', sort_order: 0, translations: { nl: 'Rood', de: 'Rot' } },
{ value: 'green', label: 'Green', sort_order: 1 }, // no translations — fallback
]
describe('Option-bearing field locale resolution', () => {
describe('FieldRadio', () => {
it('prefers translations[locale] over the default label', () => {
const wrapper = harness(FieldRadio, fieldOf(FormFieldType.RADIO, optionsSample), 'nl')
const radios = wrapper.findAll('.v-radio-stub')
expect(radios.length).toBe(2)
expect(radios[0].attributes('data-label')).toBe('Rood')
})
it('falls back to label when translation is missing for the current locale', () => {
const wrapper = harness(FieldRadio, fieldOf(FormFieldType.RADIO, optionsSample), 'nl')
const radios = wrapper.findAll('.v-radio-stub')
expect(radios[1].attributes('data-label')).toBe('Green')
})
})
describe('FieldSelect', () => {
it('emits translated title for matching locale', () => {
const wrapper = harness(FieldSelect, fieldOf(FormFieldType.SELECT, optionsSample), 'de')
const items = wrapper.findAll('.stub-item')
expect(items[0].attributes('data-title')).toBe('Rot')
})
it('falls back to label on missing translation', () => {
const wrapper = harness(FieldSelect, fieldOf(FormFieldType.SELECT, optionsSample), 'de')
const items = wrapper.findAll('.stub-item')
expect(items[1].attributes('data-title')).toBe('Green')
})
})
describe('FieldMultiselect', () => {
it('emits translated title for matching locale', () => {
const wrapper = harness(FieldMultiselect, fieldOf(FormFieldType.MULTISELECT, optionsSample), 'nl')
const items = wrapper.findAll('.stub-item')
expect(items[0].attributes('data-title')).toBe('Rood')
})
it('falls back to label on missing translation', () => {
const wrapper = harness(FieldMultiselect, fieldOf(FormFieldType.MULTISELECT, optionsSample), 'nl')
const items = wrapper.findAll('.stub-item')
expect(items[1].attributes('data-title')).toBe('Green')
})
})
describe('FieldCheckboxList', () => {
it('emits translated label for matching locale', () => {
const wrapper = harness(FieldCheckboxList, fieldOf(FormFieldType.CHECKBOX_LIST, optionsSample), 'nl')
const checkboxes = wrapper.findAll('.v-checkbox-stub')
expect(checkboxes[0].attributes('data-label')).toBe('Rood')
})
it('falls back to label on missing translation', () => {
const wrapper = harness(FieldCheckboxList, fieldOf(FormFieldType.CHECKBOX_LIST, optionsSample), 'nl')
const checkboxes = wrapper.findAll('.v-checkbox-stub')
expect(checkboxes[1].attributes('data-label')).toBe('Green')
})
})
it('default-locale fallback (no provider on the tree) uses the option label as-is', () => {
// No providePublicFormLocale in the wrapper — usePublicFormLocale
// defaults to 'nl', which doesn't appear in options that only carry
// 'de' translations, so we get the raw label.
const Wrapper = defineComponent({
setup() {
return () => h(FieldRadio, {
field: fieldOf(FormFieldType.RADIO, [
{ value: 'a', label: 'Apple', sort_order: 0, translations: { de: 'Apfel' } },
]),
modelValue: null,
})
},
})
const wrapper = mount(Wrapper, { global: { stubs } })
const radios = wrapper.findAll('.v-radio-stub')
expect(radios[0].attributes('data-label')).toBe('Apple')
})
})