mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-05-01 11:42:06 +00:00
[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:
@@ -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')
|
||||
})
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user