[test] Add component tests for some Vue Widget components (#5409)

* add component tests for vue widgets

* [refactor] improve widget test readability and type safety - addresses @DrJKL's review feedback

- Add mountComponent utility function for consistent test setup
- Add setInputValueAndTrigger helper to batch common test operations
- Replace type assertions with proper instanceof checks for type safety
- Remove duplicate test setup code to improve test readability
- Fix TypeScript errors in WidgetSlider tests

These changes address all review comments by making tests easier to read
and understand while ensuring proper type checking.

* [refactor] apply consistent test patterns to WidgetSelect.test.ts

- Add mountComponent utility function for consistent test setup
- Add setSelectValueAndEmit helper to batch select operations
- Remove repetitive mount boilerplate code throughout tests
- Maintain existing test coverage while improving readability

This ensures all widget component tests follow the same patterns
established in WidgetInputText and WidgetSlider tests.
This commit is contained in:
Christian Byrne
2025-09-06 23:08:54 -07:00
committed by GitHub
parent 5c3b67bc6b
commit 0d3b15503f
4 changed files with 671 additions and 0 deletions

View File

@@ -0,0 +1,187 @@
import { mount } from '@vue/test-utils'
import PrimeVue from 'primevue/config'
import Select from 'primevue/select'
import type { SelectProps } from 'primevue/select'
import { describe, expect, it } from 'vitest'
import type { SimplifiedWidget } from '@/types/simplifiedWidget'
import WidgetSelect from './WidgetSelect.vue'
describe('WidgetSelect Value Binding', () => {
const createMockWidget = (
value: string = 'option1',
options: Partial<
SelectProps & { values?: string[]; return_index?: boolean }
> = {},
callback?: (value: string | number | undefined) => void
): SimplifiedWidget<string | number | undefined> => ({
name: 'test_select',
type: 'combo',
value,
options: {
values: ['option1', 'option2', 'option3'],
...options
},
callback
})
const mountComponent = (
widget: SimplifiedWidget<string | number | undefined>,
modelValue: string | number | undefined,
readonly = false
) => {
return mount(WidgetSelect, {
props: {
widget,
modelValue,
readonly
},
global: {
plugins: [PrimeVue],
components: { Select }
}
})
}
const setSelectValueAndEmit = async (
wrapper: ReturnType<typeof mount>,
value: string
) => {
const select = wrapper.findComponent({ name: 'Select' })
await select.setValue(value)
return wrapper.emitted('update:modelValue')
}
describe('Vue Event Emission', () => {
it('emits Vue event when selection changes', async () => {
const widget = createMockWidget('option1')
const wrapper = mountComponent(widget, 'option1')
const emitted = await setSelectValueAndEmit(wrapper, 'option2')
expect(emitted).toBeDefined()
expect(emitted![0]).toContain('option2')
})
it('emits string value for different options', async () => {
const widget = createMockWidget('option1')
const wrapper = mountComponent(widget, 'option1')
const emitted = await setSelectValueAndEmit(wrapper, 'option3')
expect(emitted).toBeDefined()
// Should emit the string value
expect(emitted![0]).toContain('option3')
})
it('handles custom option values', async () => {
const customOptions = ['custom_a', 'custom_b', 'custom_c']
const widget = createMockWidget('custom_a', { values: customOptions })
const wrapper = mountComponent(widget, 'custom_a')
const emitted = await setSelectValueAndEmit(wrapper, 'custom_b')
expect(emitted).toBeDefined()
expect(emitted![0]).toContain('custom_b')
})
it('handles missing callback gracefully', async () => {
const widget = createMockWidget('option1', {}, undefined)
const wrapper = mountComponent(widget, 'option1')
const emitted = await setSelectValueAndEmit(wrapper, 'option2')
// Should emit Vue event
expect(emitted).toBeDefined()
expect(emitted![0]).toContain('option2')
})
it('handles value changes gracefully', async () => {
const widget = createMockWidget('option1')
const wrapper = mountComponent(widget, 'option1')
const emitted = await setSelectValueAndEmit(wrapper, 'option2')
expect(emitted).toBeDefined()
expect(emitted![0]).toContain('option2')
})
})
describe('Readonly Mode', () => {
it('disables the select component when readonly', async () => {
const widget = createMockWidget('option1')
const wrapper = mountComponent(widget, 'option1', true)
const select = wrapper.findComponent({ name: 'Select' })
expect(select.props('disabled')).toBe(true)
})
})
describe('Option Handling', () => {
it('handles empty options array', async () => {
const widget = createMockWidget('', { values: [] })
const wrapper = mountComponent(widget, '')
const select = wrapper.findComponent({ name: 'Select' })
expect(select.props('options')).toEqual([])
})
it('handles single option', async () => {
const widget = createMockWidget('only_option', {
values: ['only_option']
})
const wrapper = mountComponent(widget, 'only_option')
const select = wrapper.findComponent({ name: 'Select' })
const options = select.props('options')
expect(options).toHaveLength(1)
expect(options[0]).toEqual('only_option')
})
it('handles options with special characters', async () => {
const specialOptions = [
'option with spaces',
'option@#$%',
'option/with\\slashes'
]
const widget = createMockWidget(specialOptions[0], {
values: specialOptions
})
const wrapper = mountComponent(widget, specialOptions[0])
const emitted = await setSelectValueAndEmit(wrapper, specialOptions[1])
expect(emitted).toBeDefined()
expect(emitted![0]).toContain(specialOptions[1])
})
})
describe('Edge Cases', () => {
it('handles selection of non-existent option gracefully', async () => {
const widget = createMockWidget('option1')
const wrapper = mountComponent(widget, 'option1')
const emitted = await setSelectValueAndEmit(
wrapper,
'non_existent_option'
)
// Should still emit Vue event with the value
expect(emitted).toBeDefined()
expect(emitted![0]).toContain('non_existent_option')
})
it('handles numeric string options correctly', async () => {
const numericOptions = ['1', '2', '10', '100']
const widget = createMockWidget('1', { values: numericOptions })
const wrapper = mountComponent(widget, '1')
const emitted = await setSelectValueAndEmit(wrapper, '100')
// Should maintain string type in emitted event
expect(emitted).toBeDefined()
expect(emitted![0]).toContain('100')
})
})
})