mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-26 09:44:06 +00:00
fix tests
This commit is contained in:
@@ -2,10 +2,19 @@ import fs from 'fs-extra'
|
||||
import path from 'path'
|
||||
import { fileURLToPath } from 'url'
|
||||
|
||||
export function syncDevtools(targetComfyDir: string) {
|
||||
export function syncDevtools(targetComfyDir: string): boolean {
|
||||
if (!targetComfyDir) {
|
||||
console.warn('syncDevtools skipped: TEST_COMFYUI_DIR not set')
|
||||
return
|
||||
return false
|
||||
}
|
||||
|
||||
// Validate and sanitize the target directory path
|
||||
const resolvedTargetDir = path.resolve(targetComfyDir)
|
||||
|
||||
// Basic path validation to prevent directory traversal
|
||||
if (resolvedTargetDir.includes('..') || !path.isAbsolute(resolvedTargetDir)) {
|
||||
console.error('syncDevtools failed: Invalid target directory path')
|
||||
return false
|
||||
}
|
||||
|
||||
const moduleDir =
|
||||
@@ -19,11 +28,11 @@ export function syncDevtools(targetComfyDir: string) {
|
||||
console.warn(
|
||||
`syncDevtools skipped: source directory not found at ${devtoolsSrc}`
|
||||
)
|
||||
return
|
||||
return false
|
||||
}
|
||||
|
||||
const devtoolsDest = path.resolve(
|
||||
targetComfyDir,
|
||||
resolvedTargetDir,
|
||||
'custom_nodes',
|
||||
'ComfyUI_devtools'
|
||||
)
|
||||
@@ -35,7 +44,9 @@ export function syncDevtools(targetComfyDir: string) {
|
||||
fs.ensureDirSync(devtoolsDest)
|
||||
fs.copySync(devtoolsSrc, devtoolsDest, { overwrite: true })
|
||||
console.warn('syncDevtools: copy complete')
|
||||
return true
|
||||
} catch (error) {
|
||||
console.error(`Failed to sync DevTools to ${devtoolsDest}:`, error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,12 +5,13 @@ import PrimeVue from 'primevue/config'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import type { SimplifiedWidget } from '@/types/simplifiedWidget'
|
||||
import { createMockWidget } from '../testUtils'
|
||||
|
||||
import WidgetColorPicker from './WidgetColorPicker.vue'
|
||||
import WidgetLayoutField from './layout/WidgetLayoutField.vue'
|
||||
|
||||
describe('WidgetColorPicker Value Binding', () => {
|
||||
const createMockWidget = (
|
||||
const createLocalMockWidget = (
|
||||
value: string = '#000000',
|
||||
options: Partial<ColorPickerProps> = {},
|
||||
callback?: (value: string) => void
|
||||
@@ -54,7 +55,7 @@ describe('WidgetColorPicker Value Binding', () => {
|
||||
|
||||
describe('Vue Event Emission', () => {
|
||||
it('emits Vue event when color changes', async () => {
|
||||
const widget = createMockWidget('#ff0000')
|
||||
const widget = createLocalMockWidget('#ff0000')
|
||||
const wrapper = mountComponent(widget, '#ff0000')
|
||||
|
||||
const emitted = await setColorPickerValue(wrapper, '#00ff00')
|
||||
@@ -64,7 +65,7 @@ describe('WidgetColorPicker Value Binding', () => {
|
||||
})
|
||||
|
||||
it('handles different color formats', async () => {
|
||||
const widget = createMockWidget('#ffffff')
|
||||
const widget = createLocalMockWidget('#ffffff')
|
||||
const wrapper = mountComponent(widget, '#ffffff')
|
||||
|
||||
const emitted = await setColorPickerValue(wrapper, '#123abc')
|
||||
@@ -74,7 +75,7 @@ describe('WidgetColorPicker Value Binding', () => {
|
||||
})
|
||||
|
||||
it('handles missing callback gracefully', async () => {
|
||||
const widget = createMockWidget('#000000', {}, undefined)
|
||||
const widget = createLocalMockWidget('#000000', {}, undefined)
|
||||
const wrapper = mountComponent(widget, '#000000')
|
||||
|
||||
const emitted = await setColorPickerValue(wrapper, '#ff00ff')
|
||||
@@ -85,7 +86,7 @@ describe('WidgetColorPicker Value Binding', () => {
|
||||
})
|
||||
|
||||
it('normalizes bare hex without # to #hex on emit', async () => {
|
||||
const widget = createMockWidget('ff0000')
|
||||
const widget = createLocalMockWidget('ff0000')
|
||||
const wrapper = mountComponent(widget, 'ff0000')
|
||||
|
||||
const emitted = await setColorPickerValue(wrapper, '00ff00')
|
||||
@@ -95,7 +96,7 @@ describe('WidgetColorPicker Value Binding', () => {
|
||||
|
||||
it('normalizes rgb() strings to #hex on emit', async (context) => {
|
||||
context.skip('needs diagnosis')
|
||||
const widget = createMockWidget('#000000')
|
||||
const widget = createLocalMockWidget('#000000')
|
||||
const wrapper = mountComponent(widget, '#000000')
|
||||
|
||||
const emitted = await setColorPickerValue(wrapper, 'rgb(255, 0, 0)')
|
||||
@@ -104,7 +105,20 @@ describe('WidgetColorPicker Value Binding', () => {
|
||||
})
|
||||
|
||||
it('normalizes hsb() strings to #hex on emit', async () => {
|
||||
const widget = createMockWidget('#000000', { format: 'hsb' })
|
||||
const widget = createMockWidget<string>(
|
||||
'#000000',
|
||||
{},
|
||||
undefined,
|
||||
{
|
||||
name: 'test_color',
|
||||
type: 'color'
|
||||
},
|
||||
{
|
||||
type: 'COLOR',
|
||||
name: 'test_color',
|
||||
options: { format: 'hsb' }
|
||||
}
|
||||
)
|
||||
const wrapper = mountComponent(widget, '#000000')
|
||||
|
||||
const emitted = await setColorPickerValue(wrapper, 'hsb(120, 100, 100)')
|
||||
@@ -113,7 +127,20 @@ describe('WidgetColorPicker Value Binding', () => {
|
||||
})
|
||||
|
||||
it('normalizes HSB object values to #hex on emit', async () => {
|
||||
const widget = createMockWidget('#000000', { format: 'hsb' })
|
||||
const widget = createMockWidget<string>(
|
||||
'#000000',
|
||||
{},
|
||||
undefined,
|
||||
{
|
||||
name: 'test_color',
|
||||
type: 'color'
|
||||
},
|
||||
{
|
||||
type: 'COLOR',
|
||||
name: 'test_color',
|
||||
options: { format: 'hsb' }
|
||||
}
|
||||
)
|
||||
const wrapper = mountComponent(widget, '#000000')
|
||||
|
||||
const emitted = await setColorPickerValue(wrapper, {
|
||||
@@ -128,7 +155,7 @@ describe('WidgetColorPicker Value Binding', () => {
|
||||
|
||||
describe('Component Rendering', () => {
|
||||
it('renders color picker component', () => {
|
||||
const widget = createMockWidget('#ff0000')
|
||||
const widget = createLocalMockWidget('#ff0000')
|
||||
const wrapper = mountComponent(widget, '#ff0000')
|
||||
|
||||
const colorPicker = wrapper.findComponent({ name: 'ColorPicker' })
|
||||
@@ -137,20 +164,20 @@ describe('WidgetColorPicker Value Binding', () => {
|
||||
|
||||
it('normalizes display to a single leading #', () => {
|
||||
// Case 1: model value already includes '#'
|
||||
let widget = createMockWidget('#ff0000')
|
||||
let widget = createLocalMockWidget('#ff0000')
|
||||
let wrapper = mountComponent(widget, '#ff0000')
|
||||
let colorText = wrapper.find('[data-testid="widget-color-text"]')
|
||||
expect.soft(colorText.text()).toBe('#ff0000')
|
||||
|
||||
// Case 2: model value missing '#'
|
||||
widget = createMockWidget('ff0000')
|
||||
widget = createLocalMockWidget('ff0000')
|
||||
wrapper = mountComponent(widget, 'ff0000')
|
||||
colorText = wrapper.find('[data-testid="widget-color-text"]')
|
||||
expect.soft(colorText.text()).toBe('#ff0000')
|
||||
})
|
||||
|
||||
it('renders layout field wrapper', () => {
|
||||
const widget = createMockWidget('#ff0000')
|
||||
const widget = createLocalMockWidget('#ff0000')
|
||||
const wrapper = mountComponent(widget, '#ff0000')
|
||||
|
||||
const layoutField = wrapper.findComponent({ name: 'WidgetLayoutField' })
|
||||
@@ -158,7 +185,7 @@ describe('WidgetColorPicker Value Binding', () => {
|
||||
})
|
||||
|
||||
it('displays current color value as text', () => {
|
||||
const widget = createMockWidget('#ff0000')
|
||||
const widget = createLocalMockWidget('#ff0000')
|
||||
const wrapper = mountComponent(widget, '#ff0000')
|
||||
|
||||
const colorText = wrapper.find('[data-testid="widget-color-text"]')
|
||||
@@ -166,7 +193,7 @@ describe('WidgetColorPicker Value Binding', () => {
|
||||
})
|
||||
|
||||
it('updates color text when value changes', async () => {
|
||||
const widget = createMockWidget('#ff0000')
|
||||
const widget = createLocalMockWidget('#ff0000')
|
||||
const wrapper = mountComponent(widget, '#ff0000')
|
||||
|
||||
await setColorPickerValue(wrapper, '#00ff00')
|
||||
@@ -178,7 +205,7 @@ describe('WidgetColorPicker Value Binding', () => {
|
||||
})
|
||||
|
||||
it('uses default color when no value provided', () => {
|
||||
const widget = createMockWidget('')
|
||||
const widget = createLocalMockWidget('')
|
||||
const wrapper = mountComponent(widget, '')
|
||||
|
||||
const colorPicker = wrapper.findComponent({ name: 'ColorPicker' })
|
||||
@@ -199,7 +226,7 @@ describe('WidgetColorPicker Value Binding', () => {
|
||||
]
|
||||
|
||||
for (const color of validHexColors) {
|
||||
const widget = createMockWidget(color)
|
||||
const widget = createLocalMockWidget(color)
|
||||
const wrapper = mountComponent(widget, color)
|
||||
|
||||
const colorText = wrapper.find('[data-testid="widget-color-text"]')
|
||||
@@ -208,7 +235,7 @@ describe('WidgetColorPicker Value Binding', () => {
|
||||
})
|
||||
|
||||
it('handles short hex colors', () => {
|
||||
const widget = createMockWidget('#fff')
|
||||
const widget = createLocalMockWidget('#fff')
|
||||
const wrapper = mountComponent(widget, '#fff')
|
||||
|
||||
const colorText = wrapper.find('[data-testid="widget-color-text"]')
|
||||
@@ -220,7 +247,7 @@ describe('WidgetColorPicker Value Binding', () => {
|
||||
format: 'hex' as const,
|
||||
inline: true
|
||||
}
|
||||
const widget = createMockWidget('#ff0000', colorOptions)
|
||||
const widget = createLocalMockWidget('#ff0000', colorOptions)
|
||||
const wrapper = mountComponent(widget, '#ff0000')
|
||||
|
||||
const colorPicker = wrapper.findComponent({ name: 'ColorPicker' })
|
||||
@@ -231,7 +258,7 @@ describe('WidgetColorPicker Value Binding', () => {
|
||||
|
||||
describe('Widget Layout Integration', () => {
|
||||
it('passes widget to layout field', () => {
|
||||
const widget = createMockWidget('#ff0000')
|
||||
const widget = createLocalMockWidget('#ff0000')
|
||||
const wrapper = mountComponent(widget, '#ff0000')
|
||||
|
||||
const layoutField = wrapper.findComponent({ name: 'WidgetLayoutField' })
|
||||
@@ -239,7 +266,7 @@ describe('WidgetColorPicker Value Binding', () => {
|
||||
})
|
||||
|
||||
it('maintains proper component structure', () => {
|
||||
const widget = createMockWidget('#ff0000')
|
||||
const widget = createLocalMockWidget('#ff0000')
|
||||
const wrapper = mountComponent(widget, '#ff0000')
|
||||
|
||||
// Should have layout field containing label with color picker and text
|
||||
@@ -257,7 +284,7 @@ describe('WidgetColorPicker Value Binding', () => {
|
||||
|
||||
describe('Edge Cases', () => {
|
||||
it('handles empty color value', () => {
|
||||
const widget = createMockWidget('')
|
||||
const widget = createLocalMockWidget('')
|
||||
const wrapper = mountComponent(widget, '')
|
||||
|
||||
const colorPicker = wrapper.findComponent({ name: 'ColorPicker' })
|
||||
@@ -265,7 +292,7 @@ describe('WidgetColorPicker Value Binding', () => {
|
||||
})
|
||||
|
||||
it('handles invalid color formats gracefully', async () => {
|
||||
const widget = createMockWidget('invalid-color')
|
||||
const widget = createLocalMockWidget('invalid-color')
|
||||
const wrapper = mountComponent(widget, 'invalid-color')
|
||||
|
||||
const colorText = wrapper.find('[data-testid="widget-color-text"]')
|
||||
@@ -277,7 +304,7 @@ describe('WidgetColorPicker Value Binding', () => {
|
||||
})
|
||||
|
||||
it('handles widget with no options', () => {
|
||||
const widget = createMockWidget('#ff0000')
|
||||
const widget = createLocalMockWidget('#ff0000')
|
||||
const wrapper = mountComponent(widget, '#ff0000')
|
||||
|
||||
const colorPicker = wrapper.findComponent({ name: 'ColorPicker' })
|
||||
|
||||
@@ -68,11 +68,18 @@ describe('WidgetFileUpload File Handling', () => {
|
||||
it('renders file input with correct attributes', () => {
|
||||
const widget = createMockWidget<File[] | null>(
|
||||
null,
|
||||
{ accept: 'image/*' },
|
||||
{},
|
||||
undefined,
|
||||
{
|
||||
name: 'test_file_upload',
|
||||
type: 'file'
|
||||
},
|
||||
{
|
||||
type: 'FILEUPLOAD',
|
||||
name: 'test_file_upload',
|
||||
options: {
|
||||
accept: 'image/*'
|
||||
}
|
||||
}
|
||||
)
|
||||
const wrapper = mountComponent(widget, null)
|
||||
|
||||
@@ -6,6 +6,7 @@ import { describe, expect, it } from 'vitest'
|
||||
import { createI18n } from 'vue-i18n'
|
||||
|
||||
import type { SimplifiedWidget } from '@/types/simplifiedWidget'
|
||||
import { createMockWidget } from '../testUtils'
|
||||
|
||||
import WidgetGalleria from './WidgetGalleria.vue'
|
||||
import type { GalleryImage, GalleryValue } from './WidgetGalleria.vue'
|
||||
@@ -45,7 +46,7 @@ const TEST_IMAGE_OBJECTS: readonly GalleryImage[] = Object.freeze([
|
||||
])
|
||||
|
||||
// Helper functions outside describe blocks for better clarity
|
||||
function createMockWidget(
|
||||
function createLocalMockWidget(
|
||||
value: GalleryValue = [],
|
||||
options: Partial<GalleriaProps> = {}
|
||||
): SimplifiedWidget<GalleryValue> {
|
||||
@@ -85,7 +86,20 @@ function createGalleriaWrapper(
|
||||
images: GalleryValue,
|
||||
options: Partial<GalleriaProps> = {}
|
||||
) {
|
||||
const widget = createMockWidget(images, options)
|
||||
const widget = createMockWidget<GalleryValue>(
|
||||
images,
|
||||
{},
|
||||
undefined,
|
||||
{
|
||||
name: 'test_galleria',
|
||||
type: 'array'
|
||||
},
|
||||
{
|
||||
type: 'GALLERIA',
|
||||
name: 'test_galleria',
|
||||
options: options
|
||||
}
|
||||
)
|
||||
return mountComponent(widget, images)
|
||||
}
|
||||
|
||||
@@ -101,7 +115,7 @@ describe('WidgetGalleria Image Display', () => {
|
||||
})
|
||||
|
||||
it('displays empty gallery when no images provided', () => {
|
||||
const widget = createMockWidget([])
|
||||
const widget = createLocalMockWidget([])
|
||||
const wrapper = mountComponent(widget, [])
|
||||
|
||||
const galleria = wrapper.findComponent({ name: 'Galleria' })
|
||||
@@ -109,7 +123,7 @@ describe('WidgetGalleria Image Display', () => {
|
||||
})
|
||||
|
||||
it('handles null or undefined value gracefully', () => {
|
||||
const widget = createMockWidget([])
|
||||
const widget = createLocalMockWidget([])
|
||||
const wrapper = mountComponent(widget, [])
|
||||
|
||||
const galleria = wrapper.findComponent({ name: 'Galleria' })
|
||||
@@ -119,7 +133,7 @@ describe('WidgetGalleria Image Display', () => {
|
||||
|
||||
describe('String Array Input', () => {
|
||||
it('converts string array to image objects', () => {
|
||||
const widget = createMockWidget([...TEST_IMAGES_SMALL])
|
||||
const widget = createLocalMockWidget([...TEST_IMAGES_SMALL])
|
||||
const wrapper = mountComponent(widget, [...TEST_IMAGES_SMALL])
|
||||
|
||||
const galleria = wrapper.findComponent({ name: 'Galleria' })
|
||||
@@ -134,7 +148,7 @@ describe('WidgetGalleria Image Display', () => {
|
||||
})
|
||||
|
||||
it('handles single string image', () => {
|
||||
const widget = createMockWidget([...TEST_IMAGES_SINGLE])
|
||||
const widget = createLocalMockWidget([...TEST_IMAGES_SINGLE])
|
||||
const wrapper = mountComponent(widget, [...TEST_IMAGES_SINGLE])
|
||||
|
||||
const galleria = wrapper.findComponent({ name: 'Galleria' })
|
||||
@@ -151,7 +165,7 @@ describe('WidgetGalleria Image Display', () => {
|
||||
|
||||
describe('Object Array Input', () => {
|
||||
it('preserves image objects as-is', () => {
|
||||
const widget = createMockWidget([...TEST_IMAGE_OBJECTS])
|
||||
const widget = createLocalMockWidget([...TEST_IMAGE_OBJECTS])
|
||||
const wrapper = mountComponent(widget, [...TEST_IMAGE_OBJECTS])
|
||||
|
||||
const galleria = wrapper.findComponent({ name: 'Galleria' })
|
||||
@@ -166,7 +180,7 @@ describe('WidgetGalleria Image Display', () => {
|
||||
{ itemImageSrc: 'https://example.com/image2.jpg' },
|
||||
{ thumbnailImageSrc: 'https://example.com/thumb3.jpg' }
|
||||
]
|
||||
const widget = createMockWidget(images)
|
||||
const widget = createLocalMockWidget(images)
|
||||
const wrapper = mountComponent(widget, images)
|
||||
|
||||
const galleria = wrapper.findComponent({ name: 'Galleria' })
|
||||
@@ -227,7 +241,20 @@ describe('WidgetGalleria Image Display', () => {
|
||||
|
||||
it('respects widget option to hide navigation buttons', () => {
|
||||
const images = createImageStrings(3)
|
||||
const widget = createMockWidget(images, { showItemNavigators: false })
|
||||
const widget = createMockWidget<GalleryValue>(
|
||||
images,
|
||||
{},
|
||||
undefined,
|
||||
{
|
||||
name: 'test_galleria',
|
||||
type: 'array'
|
||||
},
|
||||
{
|
||||
type: 'GALLERIA',
|
||||
name: 'test_galleria',
|
||||
options: { showItemNavigators: false }
|
||||
}
|
||||
)
|
||||
const wrapper = mountComponent(widget, images)
|
||||
|
||||
const galleria = wrapper.findComponent({ name: 'Galleria' })
|
||||
@@ -236,7 +263,20 @@ describe('WidgetGalleria Image Display', () => {
|
||||
|
||||
it('shows navigation buttons when explicitly enabled for multiple images', () => {
|
||||
const images = createImageStrings(3)
|
||||
const widget = createMockWidget(images, { showItemNavigators: true })
|
||||
const widget = createMockWidget<GalleryValue>(
|
||||
images,
|
||||
{},
|
||||
undefined,
|
||||
{
|
||||
name: 'test_galleria',
|
||||
type: 'array'
|
||||
},
|
||||
{
|
||||
type: 'GALLERIA',
|
||||
name: 'test_galleria',
|
||||
options: { showItemNavigators: true }
|
||||
}
|
||||
)
|
||||
const wrapper = mountComponent(widget, images)
|
||||
|
||||
const galleria = wrapper.findComponent({ name: 'Galleria' })
|
||||
@@ -247,7 +287,7 @@ describe('WidgetGalleria Image Display', () => {
|
||||
describe('Widget Options Handling', () => {
|
||||
it('passes through valid widget options', () => {
|
||||
const images = createImageStrings(2)
|
||||
const widget = createMockWidget(images, {
|
||||
const widget = createLocalMockWidget(images, {
|
||||
circular: true,
|
||||
autoPlay: true,
|
||||
transitionInterval: 3000
|
||||
@@ -262,7 +302,7 @@ describe('WidgetGalleria Image Display', () => {
|
||||
|
||||
it('applies custom styling props', () => {
|
||||
const images = createImageStrings(2)
|
||||
const widget = createMockWidget(images)
|
||||
const widget = createLocalMockWidget(images)
|
||||
const wrapper = mountComponent(widget, images)
|
||||
|
||||
const galleria = wrapper.findComponent({ name: 'Galleria' })
|
||||
@@ -274,7 +314,7 @@ describe('WidgetGalleria Image Display', () => {
|
||||
describe('Active Index Management', () => {
|
||||
it('initializes with zero active index', () => {
|
||||
const images = createImageStrings(3)
|
||||
const widget = createMockWidget(images)
|
||||
const widget = createLocalMockWidget(images)
|
||||
const wrapper = mountComponent(widget, images)
|
||||
|
||||
const galleria = wrapper.findComponent({ name: 'Galleria' })
|
||||
@@ -283,7 +323,7 @@ describe('WidgetGalleria Image Display', () => {
|
||||
|
||||
it('can update active index', async () => {
|
||||
const images = createImageStrings(3)
|
||||
const widget = createMockWidget(images)
|
||||
const widget = createLocalMockWidget(images)
|
||||
const wrapper = mountComponent(widget, images)
|
||||
|
||||
const galleria = wrapper.findComponent({ name: 'Galleria' })
|
||||
@@ -304,7 +344,7 @@ describe('WidgetGalleria Image Display', () => {
|
||||
},
|
||||
{ src: 'https://example.com/only-src.jpg' }
|
||||
]
|
||||
const widget = createMockWidget(images)
|
||||
const widget = createLocalMockWidget(images)
|
||||
const wrapper = mountComponent(widget, images)
|
||||
|
||||
// The template logic should prioritize itemImageSrc > src > fallback to the item itself
|
||||
@@ -320,7 +360,7 @@ describe('WidgetGalleria Image Display', () => {
|
||||
},
|
||||
{ src: 'https://example.com/only-src.jpg' }
|
||||
]
|
||||
const widget = createMockWidget(images)
|
||||
const widget = createLocalMockWidget(images)
|
||||
const wrapper = mountComponent(widget, images)
|
||||
|
||||
// The template logic should prioritize thumbnailImageSrc > src > fallback to the item itself
|
||||
@@ -331,7 +371,7 @@ describe('WidgetGalleria Image Display', () => {
|
||||
|
||||
describe('Edge Cases', () => {
|
||||
it('handles empty array gracefully', () => {
|
||||
const widget = createMockWidget([])
|
||||
const widget = createLocalMockWidget([])
|
||||
const wrapper = mountComponent(widget, [])
|
||||
|
||||
const galleria = wrapper.findComponent({ name: 'Galleria' })
|
||||
@@ -347,7 +387,7 @@ describe('WidgetGalleria Image Display', () => {
|
||||
null, // Null value
|
||||
undefined // Undefined value
|
||||
]
|
||||
const widget = createMockWidget(malformedImages as string[])
|
||||
const widget = createLocalMockWidget(malformedImages as string[])
|
||||
const wrapper = mountComponent(widget, malformedImages as string[])
|
||||
|
||||
const galleria = wrapper.findComponent({ name: 'Galleria' })
|
||||
@@ -358,7 +398,7 @@ describe('WidgetGalleria Image Display', () => {
|
||||
|
||||
it('handles very large image arrays', () => {
|
||||
const largeImageArray = createImageStrings(100)
|
||||
const widget = createMockWidget(largeImageArray)
|
||||
const widget = createLocalMockWidget(largeImageArray)
|
||||
const wrapper = mountComponent(widget, largeImageArray)
|
||||
|
||||
const galleria = wrapper.findComponent({ name: 'Galleria' })
|
||||
@@ -374,7 +414,7 @@ describe('WidgetGalleria Image Display', () => {
|
||||
{ itemImageSrc: 'https://example.com/object.jpg' },
|
||||
'https://example.com/another-string.jpg'
|
||||
]
|
||||
const widget = createMockWidget(mixedArray as string[])
|
||||
const widget = createLocalMockWidget(mixedArray as string[])
|
||||
|
||||
// The component expects consistent typing, but let's test it handles mixed input
|
||||
expect(() => mountComponent(widget, mixedArray as string[])).not.toThrow()
|
||||
@@ -382,7 +422,7 @@ describe('WidgetGalleria Image Display', () => {
|
||||
|
||||
it('handles invalid URL strings', () => {
|
||||
const invalidUrls = ['not-a-url', '', ' ', 'http://', 'ftp://invalid']
|
||||
const widget = createMockWidget(invalidUrls)
|
||||
const widget = createLocalMockWidget(invalidUrls)
|
||||
const wrapper = mountComponent(widget, invalidUrls)
|
||||
|
||||
const galleria = wrapper.findComponent({ name: 'Galleria' })
|
||||
@@ -393,7 +433,7 @@ describe('WidgetGalleria Image Display', () => {
|
||||
describe('Styling and Layout', () => {
|
||||
it('applies max-width constraint', () => {
|
||||
const images = createImageStrings(2)
|
||||
const widget = createMockWidget(images)
|
||||
const widget = createLocalMockWidget(images)
|
||||
const wrapper = mountComponent(widget, images)
|
||||
|
||||
const galleria = wrapper.findComponent({ name: 'Galleria' })
|
||||
@@ -403,7 +443,7 @@ describe('WidgetGalleria Image Display', () => {
|
||||
|
||||
it('applies passthrough props for thumbnails', () => {
|
||||
const images = createImageStrings(3)
|
||||
const widget = createMockWidget(images)
|
||||
const widget = createLocalMockWidget(images)
|
||||
const wrapper = mountComponent(widget, images)
|
||||
|
||||
const galleria = wrapper.findComponent({ name: 'Galleria' })
|
||||
|
||||
@@ -105,9 +105,14 @@ const showThumbnails = computed(() => {
|
||||
if (!spec || !isGalleriaInputSpec(spec)) {
|
||||
return galleryImages.value.length > 1
|
||||
}
|
||||
return (
|
||||
spec.options?.showThumbnails !== false && galleryImages.value.length > 1
|
||||
)
|
||||
|
||||
// If explicitly set to false, respect that regardless of image count
|
||||
if (spec.options?.showThumbnails === false) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Otherwise show thumbnails if multiple images (or if explicitly set to true)
|
||||
return galleryImages.value.length > 1
|
||||
})
|
||||
|
||||
const showNavButtons = computed(() => {
|
||||
@@ -115,9 +120,14 @@ const showNavButtons = computed(() => {
|
||||
if (!spec || !isGalleriaInputSpec(spec)) {
|
||||
return galleryImages.value.length > 1
|
||||
}
|
||||
return (
|
||||
spec.options?.showItemNavigators !== false && galleryImages.value.length > 1
|
||||
)
|
||||
|
||||
// If explicitly set to false, respect that regardless of image count
|
||||
if (spec.options?.showItemNavigators === false) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Otherwise show navigation buttons if multiple images (or if explicitly set to true)
|
||||
return galleryImages.value.length > 1
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
@@ -5,11 +5,12 @@ import type { MultiSelectProps } from 'primevue/multiselect'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import type { SimplifiedWidget, WidgetValue } from '@/types/simplifiedWidget'
|
||||
import { createMockWidget } from '../testUtils'
|
||||
|
||||
import WidgetMultiSelect from './WidgetMultiSelect.vue'
|
||||
|
||||
describe('WidgetMultiSelect Value Binding', () => {
|
||||
const createMockWidget = (
|
||||
const createLocalMockWidget = (
|
||||
value: WidgetValue[] = [],
|
||||
options: Partial<MultiSelectProps> & { values?: WidgetValue[] } = {},
|
||||
callback?: (value: WidgetValue[]) => void
|
||||
@@ -50,9 +51,17 @@ describe('WidgetMultiSelect Value Binding', () => {
|
||||
|
||||
describe('Vue Event Emission', () => {
|
||||
it('emits Vue event when selection changes', async () => {
|
||||
const widget = createMockWidget([], {
|
||||
values: ['option1', 'option2', 'option3']
|
||||
})
|
||||
const widget = createMockWidget<WidgetValue[]>(
|
||||
[],
|
||||
{},
|
||||
undefined,
|
||||
{},
|
||||
{
|
||||
type: 'MULTISELECT',
|
||||
name: 'test_widget',
|
||||
options: { values: ['option1', 'option2', 'option3'] }
|
||||
}
|
||||
)
|
||||
const wrapper = mountComponent(widget, [])
|
||||
|
||||
await setMultiSelectValueAndEmit(wrapper, ['option1', 'option2'])
|
||||
@@ -63,9 +72,17 @@ describe('WidgetMultiSelect Value Binding', () => {
|
||||
})
|
||||
|
||||
it('emits Vue event when selection is cleared', async () => {
|
||||
const widget = createMockWidget(['option1'], {
|
||||
values: ['option1', 'option2']
|
||||
})
|
||||
const widget = createMockWidget<WidgetValue[]>(
|
||||
['option1'],
|
||||
{},
|
||||
undefined,
|
||||
{},
|
||||
{
|
||||
type: 'MULTISELECT',
|
||||
name: 'test_widget',
|
||||
options: { values: ['option1', 'option2'] }
|
||||
}
|
||||
)
|
||||
const wrapper = mountComponent(widget, ['option1'])
|
||||
|
||||
await setMultiSelectValueAndEmit(wrapper, [])
|
||||
@@ -76,7 +93,7 @@ describe('WidgetMultiSelect Value Binding', () => {
|
||||
})
|
||||
|
||||
it('handles single item selection', async () => {
|
||||
const widget = createMockWidget([], {
|
||||
const widget = createLocalMockWidget([], {
|
||||
values: ['single']
|
||||
})
|
||||
const wrapper = mountComponent(widget, [])
|
||||
@@ -89,7 +106,7 @@ describe('WidgetMultiSelect Value Binding', () => {
|
||||
})
|
||||
|
||||
it('emits update:modelValue for callback handling at parent level', async () => {
|
||||
const widget = createMockWidget([], {
|
||||
const widget = createLocalMockWidget([], {
|
||||
values: ['option1', 'option2']
|
||||
})
|
||||
const wrapper = mountComponent(widget, [])
|
||||
@@ -103,7 +120,7 @@ describe('WidgetMultiSelect Value Binding', () => {
|
||||
})
|
||||
|
||||
it('handles missing callback gracefully', async () => {
|
||||
const widget = createMockWidget(
|
||||
const widget = createLocalMockWidget(
|
||||
[],
|
||||
{
|
||||
values: ['option1']
|
||||
@@ -123,7 +140,7 @@ describe('WidgetMultiSelect Value Binding', () => {
|
||||
|
||||
describe('Component Rendering', () => {
|
||||
it('renders multiselect component', () => {
|
||||
const widget = createMockWidget([], {
|
||||
const widget = createLocalMockWidget([], {
|
||||
values: ['option1', 'option2']
|
||||
})
|
||||
const wrapper = mountComponent(widget, [])
|
||||
@@ -134,7 +151,17 @@ describe('WidgetMultiSelect Value Binding', () => {
|
||||
|
||||
it('displays options from widget values', () => {
|
||||
const options = ['apple', 'banana', 'cherry']
|
||||
const widget = createMockWidget([], { values: options })
|
||||
const widget = createMockWidget<WidgetValue[]>(
|
||||
[],
|
||||
{},
|
||||
undefined,
|
||||
{},
|
||||
{
|
||||
type: 'MULTISELECT',
|
||||
name: 'test_widget',
|
||||
options: { values: options }
|
||||
}
|
||||
)
|
||||
const wrapper = mountComponent(widget, [])
|
||||
|
||||
const multiselect = wrapper.findComponent({ name: 'MultiSelect' })
|
||||
@@ -142,9 +169,17 @@ describe('WidgetMultiSelect Value Binding', () => {
|
||||
})
|
||||
|
||||
it('displays initial selected values', () => {
|
||||
const widget = createMockWidget(['banana'], {
|
||||
values: ['apple', 'banana', 'cherry']
|
||||
})
|
||||
const widget = createMockWidget<WidgetValue[]>(
|
||||
['banana'],
|
||||
{},
|
||||
undefined,
|
||||
{},
|
||||
{
|
||||
type: 'MULTISELECT',
|
||||
name: 'test_widget',
|
||||
options: { values: ['apple', 'banana', 'cherry'] }
|
||||
}
|
||||
)
|
||||
const wrapper = mountComponent(widget, ['banana'])
|
||||
|
||||
const multiselect = wrapper.findComponent({ name: 'MultiSelect' })
|
||||
@@ -152,7 +187,17 @@ describe('WidgetMultiSelect Value Binding', () => {
|
||||
})
|
||||
|
||||
it('applies small size styling', () => {
|
||||
const widget = createMockWidget([], { values: ['test'] })
|
||||
const widget = createMockWidget<WidgetValue[]>(
|
||||
[],
|
||||
{},
|
||||
undefined,
|
||||
{},
|
||||
{
|
||||
type: 'MULTISELECT',
|
||||
name: 'test_widget',
|
||||
options: { values: ['test'] }
|
||||
}
|
||||
)
|
||||
const wrapper = mountComponent(widget, [])
|
||||
|
||||
const multiselect = wrapper.findComponent({ name: 'MultiSelect' })
|
||||
@@ -160,7 +205,17 @@ describe('WidgetMultiSelect Value Binding', () => {
|
||||
})
|
||||
|
||||
it('uses chip display mode', () => {
|
||||
const widget = createMockWidget([], { values: ['test'] })
|
||||
const widget = createMockWidget<WidgetValue[]>(
|
||||
[],
|
||||
{},
|
||||
undefined,
|
||||
{},
|
||||
{
|
||||
type: 'MULTISELECT',
|
||||
name: 'test_widget',
|
||||
options: { values: ['test'] }
|
||||
}
|
||||
)
|
||||
const wrapper = mountComponent(widget, [])
|
||||
|
||||
const multiselect = wrapper.findComponent({ name: 'MultiSelect' })
|
||||
@@ -168,7 +223,17 @@ describe('WidgetMultiSelect Value Binding', () => {
|
||||
})
|
||||
|
||||
it('applies text-xs class', () => {
|
||||
const widget = createMockWidget([], { values: ['test'] })
|
||||
const widget = createMockWidget<WidgetValue[]>(
|
||||
[],
|
||||
{},
|
||||
undefined,
|
||||
{},
|
||||
{
|
||||
type: 'MULTISELECT',
|
||||
name: 'test_widget',
|
||||
options: { values: ['test'] }
|
||||
}
|
||||
)
|
||||
const wrapper = mountComponent(widget, [])
|
||||
|
||||
const multiselect = wrapper.findComponent({ name: 'MultiSelect' })
|
||||
@@ -178,7 +243,7 @@ describe('WidgetMultiSelect Value Binding', () => {
|
||||
|
||||
describe('Widget Options Handling', () => {
|
||||
it('passes through valid widget options', () => {
|
||||
const widget = createMockWidget([], {
|
||||
const widget = createLocalMockWidget([], {
|
||||
values: ['option1', 'option2'],
|
||||
placeholder: 'Select items...',
|
||||
filter: true,
|
||||
@@ -193,7 +258,7 @@ describe('WidgetMultiSelect Value Binding', () => {
|
||||
})
|
||||
|
||||
it('excludes panel-related props', () => {
|
||||
const widget = createMockWidget([], {
|
||||
const widget = createLocalMockWidget([], {
|
||||
values: ['option1'],
|
||||
overlayStyle: { color: 'red' },
|
||||
panelClass: 'custom-panel'
|
||||
@@ -207,7 +272,17 @@ describe('WidgetMultiSelect Value Binding', () => {
|
||||
})
|
||||
|
||||
it('handles empty values array', () => {
|
||||
const widget = createMockWidget([], { values: [] })
|
||||
const widget = createMockWidget<WidgetValue[]>(
|
||||
[],
|
||||
{},
|
||||
undefined,
|
||||
{},
|
||||
{
|
||||
type: 'MULTISELECT',
|
||||
name: 'test_widget',
|
||||
options: { values: [] }
|
||||
}
|
||||
)
|
||||
const wrapper = mountComponent(widget, [])
|
||||
|
||||
const multiselect = wrapper.findComponent({ name: 'MultiSelect' })
|
||||
@@ -215,7 +290,7 @@ describe('WidgetMultiSelect Value Binding', () => {
|
||||
})
|
||||
|
||||
it('handles missing values option', () => {
|
||||
const widget = createMockWidget([])
|
||||
const widget = createLocalMockWidget([])
|
||||
const wrapper = mountComponent(widget, [])
|
||||
|
||||
const multiselect = wrapper.findComponent({ name: 'MultiSelect' })
|
||||
@@ -226,7 +301,7 @@ describe('WidgetMultiSelect Value Binding', () => {
|
||||
|
||||
describe('Edge Cases', () => {
|
||||
it('handles numeric values', async () => {
|
||||
const widget = createMockWidget([], {
|
||||
const widget = createLocalMockWidget([], {
|
||||
values: [1, 2, 3, 4, 5]
|
||||
})
|
||||
const wrapper = mountComponent(widget, [])
|
||||
@@ -239,7 +314,7 @@ describe('WidgetMultiSelect Value Binding', () => {
|
||||
})
|
||||
|
||||
it('handles mixed type values', async () => {
|
||||
const widget = createMockWidget([], {
|
||||
const widget = createLocalMockWidget([], {
|
||||
values: ['string', 123, true, null]
|
||||
})
|
||||
const wrapper = mountComponent(widget, [])
|
||||
@@ -256,7 +331,7 @@ describe('WidgetMultiSelect Value Binding', () => {
|
||||
{ id: 1, label: 'First' },
|
||||
{ id: 2, label: 'Second' }
|
||||
]
|
||||
const widget = createMockWidget([], {
|
||||
const widget = createLocalMockWidget([], {
|
||||
values: objectValues,
|
||||
optionLabel: 'label',
|
||||
optionValue: 'id'
|
||||
@@ -271,7 +346,7 @@ describe('WidgetMultiSelect Value Binding', () => {
|
||||
})
|
||||
|
||||
it('handles duplicate selections gracefully', async () => {
|
||||
const widget = createMockWidget([], {
|
||||
const widget = createLocalMockWidget([], {
|
||||
values: ['option1', 'option2']
|
||||
})
|
||||
const wrapper = mountComponent(widget, [])
|
||||
@@ -290,7 +365,17 @@ describe('WidgetMultiSelect Value Binding', () => {
|
||||
{ length: 1000 },
|
||||
(_, i) => `option${i}`
|
||||
)
|
||||
const widget = createMockWidget([], { values: largeOptionList })
|
||||
const widget = createMockWidget<WidgetValue[]>(
|
||||
[],
|
||||
{},
|
||||
undefined,
|
||||
{},
|
||||
{
|
||||
type: 'MULTISELECT',
|
||||
name: 'test_widget',
|
||||
options: { values: largeOptionList }
|
||||
}
|
||||
)
|
||||
const wrapper = mountComponent(widget, [])
|
||||
|
||||
const multiselect = wrapper.findComponent({ name: 'MultiSelect' })
|
||||
@@ -298,7 +383,7 @@ describe('WidgetMultiSelect Value Binding', () => {
|
||||
})
|
||||
|
||||
it('handles empty string values', async () => {
|
||||
const widget = createMockWidget([], {
|
||||
const widget = createLocalMockWidget([], {
|
||||
values: ['', 'not empty', ' ', 'normal']
|
||||
})
|
||||
const wrapper = mountComponent(widget, [])
|
||||
@@ -313,7 +398,17 @@ describe('WidgetMultiSelect Value Binding', () => {
|
||||
|
||||
describe('Integration with Layout', () => {
|
||||
it('renders within WidgetLayoutField', () => {
|
||||
const widget = createMockWidget([], { values: ['test'] })
|
||||
const widget = createMockWidget<WidgetValue[]>(
|
||||
[],
|
||||
{},
|
||||
undefined,
|
||||
{},
|
||||
{
|
||||
type: 'MULTISELECT',
|
||||
name: 'test_widget',
|
||||
options: { values: ['test'] }
|
||||
}
|
||||
)
|
||||
const wrapper = mountComponent(widget, [])
|
||||
|
||||
const layoutField = wrapper.findComponent({ name: 'WidgetLayoutField' })
|
||||
@@ -322,7 +417,17 @@ describe('WidgetMultiSelect Value Binding', () => {
|
||||
})
|
||||
|
||||
it('passes widget name to layout field', () => {
|
||||
const widget = createMockWidget([], { values: ['test'] })
|
||||
const widget = createMockWidget<WidgetValue[]>(
|
||||
[],
|
||||
{},
|
||||
undefined,
|
||||
{},
|
||||
{
|
||||
type: 'MULTISELECT',
|
||||
name: 'test_widget',
|
||||
options: { values: ['test'] }
|
||||
}
|
||||
)
|
||||
widget.name = 'custom_multiselect'
|
||||
const wrapper = mountComponent(widget, [])
|
||||
|
||||
|
||||
@@ -3,23 +3,10 @@ import PrimeVue from 'primevue/config'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import type { SimplifiedWidget } from '@/types/simplifiedWidget'
|
||||
import { createMockWidget } from '../testUtils'
|
||||
|
||||
import WidgetSelectButton from './WidgetSelectButton.vue'
|
||||
|
||||
function createMockWidget(
|
||||
value: string = 'option1',
|
||||
options: SimplifiedWidget['options'] = {},
|
||||
callback?: (value: string) => void
|
||||
): SimplifiedWidget<string> {
|
||||
return {
|
||||
name: 'test_selectbutton',
|
||||
type: 'string',
|
||||
value,
|
||||
options,
|
||||
callback
|
||||
}
|
||||
}
|
||||
|
||||
function mountComponent(
|
||||
widget: SimplifiedWidget<string>,
|
||||
modelValue: string,
|
||||
@@ -57,9 +44,20 @@ async function clickSelectButton(
|
||||
describe('WidgetSelectButton Button Selection', () => {
|
||||
describe('Basic Rendering', () => {
|
||||
it('renders FormSelectButton component', () => {
|
||||
const widget = createMockWidget('option1', {
|
||||
values: ['option1', 'option2', 'option3']
|
||||
})
|
||||
const widget = createMockWidget<string>(
|
||||
'option1',
|
||||
{},
|
||||
undefined,
|
||||
{
|
||||
name: 'test_selectbutton',
|
||||
type: 'string'
|
||||
},
|
||||
{
|
||||
type: 'SELECTBUTTON',
|
||||
name: 'test_selectbutton',
|
||||
options: { values: ['option1', 'option2', 'option3'] }
|
||||
}
|
||||
)
|
||||
const wrapper = mountComponent(widget, 'option1')
|
||||
|
||||
const formSelectButton = wrapper.findComponent({
|
||||
@@ -70,7 +68,20 @@ describe('WidgetSelectButton Button Selection', () => {
|
||||
|
||||
it('renders buttons for each option', () => {
|
||||
const options = ['first', 'second', 'third']
|
||||
const widget = createMockWidget('first', { values: options })
|
||||
const widget = createMockWidget<string>(
|
||||
'first',
|
||||
{},
|
||||
undefined,
|
||||
{
|
||||
name: 'test_selectbutton',
|
||||
type: 'string'
|
||||
},
|
||||
{
|
||||
type: 'SELECTBUTTON',
|
||||
name: 'test_selectbutton',
|
||||
options: { values: options }
|
||||
}
|
||||
)
|
||||
const wrapper = mountComponent(widget, 'first')
|
||||
|
||||
const buttons = wrapper.findAll('button')
|
||||
@@ -81,7 +92,20 @@ describe('WidgetSelectButton Button Selection', () => {
|
||||
})
|
||||
|
||||
it('handles empty options array', () => {
|
||||
const widget = createMockWidget('', { values: [] })
|
||||
const widget = createMockWidget<string>(
|
||||
'',
|
||||
{},
|
||||
undefined,
|
||||
{
|
||||
name: 'test_selectbutton',
|
||||
type: 'string'
|
||||
},
|
||||
{
|
||||
type: 'SELECTBUTTON',
|
||||
name: 'test_selectbutton',
|
||||
options: { values: [] }
|
||||
}
|
||||
)
|
||||
const wrapper = mountComponent(widget, '')
|
||||
|
||||
const buttons = wrapper.findAll('button')
|
||||
@@ -89,7 +113,7 @@ describe('WidgetSelectButton Button Selection', () => {
|
||||
})
|
||||
|
||||
it('handles missing values option', () => {
|
||||
const widget = createMockWidget('')
|
||||
const widget = createMockWidget<string>('')
|
||||
const wrapper = mountComponent(widget, '')
|
||||
|
||||
const buttons = wrapper.findAll('button')
|
||||
@@ -100,7 +124,20 @@ describe('WidgetSelectButton Button Selection', () => {
|
||||
describe('Selection State', () => {
|
||||
it('highlights selected option', () => {
|
||||
const options = ['apple', 'banana', 'cherry']
|
||||
const widget = createMockWidget('banana', { values: options })
|
||||
const widget = createMockWidget<string>(
|
||||
'banana',
|
||||
{},
|
||||
undefined,
|
||||
{
|
||||
name: 'test_selectbutton',
|
||||
type: 'string'
|
||||
},
|
||||
{
|
||||
type: 'SELECTBUTTON',
|
||||
name: 'test_selectbutton',
|
||||
options: { values: options }
|
||||
}
|
||||
)
|
||||
const wrapper = mountComponent(widget, 'banana')
|
||||
|
||||
const buttons = wrapper.findAll('button')
|
||||
@@ -119,7 +156,20 @@ describe('WidgetSelectButton Button Selection', () => {
|
||||
|
||||
it('handles no selection gracefully', () => {
|
||||
const options = ['option1', 'option2']
|
||||
const widget = createMockWidget('nonexistent', { values: options })
|
||||
const widget = createMockWidget<string>(
|
||||
'nonexistent',
|
||||
{},
|
||||
undefined,
|
||||
{
|
||||
name: 'test_selectbutton',
|
||||
type: 'string'
|
||||
},
|
||||
{
|
||||
type: 'SELECTBUTTON',
|
||||
name: 'test_selectbutton',
|
||||
options: { values: options }
|
||||
}
|
||||
)
|
||||
const wrapper = mountComponent(widget, 'nonexistent')
|
||||
|
||||
const buttons = wrapper.findAll('button')
|
||||
@@ -135,7 +185,20 @@ describe('WidgetSelectButton Button Selection', () => {
|
||||
context.skip('Classes not updating, needs diagnosis')
|
||||
|
||||
const options = ['first', 'second', 'third']
|
||||
const widget = createMockWidget('first', { values: options })
|
||||
const widget = createMockWidget<string>(
|
||||
'first',
|
||||
{},
|
||||
undefined,
|
||||
{
|
||||
name: 'test_selectbutton',
|
||||
type: 'string'
|
||||
},
|
||||
{
|
||||
type: 'SELECTBUTTON',
|
||||
name: 'test_selectbutton',
|
||||
options: { values: options }
|
||||
}
|
||||
)
|
||||
const wrapper = mountComponent(widget, 'first')
|
||||
|
||||
// Initially 'first' is selected
|
||||
@@ -159,7 +222,20 @@ describe('WidgetSelectButton Button Selection', () => {
|
||||
describe('User Interactions', () => {
|
||||
it('emits update:modelValue when button is clicked', async () => {
|
||||
const options = ['first', 'second', 'third']
|
||||
const widget = createMockWidget('first', { values: options })
|
||||
const widget = createMockWidget<string>(
|
||||
'first',
|
||||
{},
|
||||
undefined,
|
||||
{
|
||||
name: 'test_selectbutton',
|
||||
type: 'string'
|
||||
},
|
||||
{
|
||||
type: 'SELECTBUTTON',
|
||||
name: 'test_selectbutton',
|
||||
options: { values: options }
|
||||
}
|
||||
)
|
||||
const wrapper = mountComponent(widget, 'first')
|
||||
|
||||
await clickSelectButton(wrapper, 'second')
|
||||
@@ -173,10 +249,19 @@ describe('WidgetSelectButton Button Selection', () => {
|
||||
context.skip('Callback is not being called, needs diagnosis')
|
||||
const mockCallback = vi.fn()
|
||||
const options = ['option1', 'option2']
|
||||
const widget = createMockWidget(
|
||||
const widget = createMockWidget<string>(
|
||||
'option1',
|
||||
{ values: options },
|
||||
mockCallback
|
||||
{},
|
||||
mockCallback,
|
||||
{
|
||||
name: 'test_selectbutton',
|
||||
type: 'string'
|
||||
},
|
||||
{
|
||||
type: 'SELECTBUTTON',
|
||||
name: 'test_selectbutton',
|
||||
options: { values: options }
|
||||
}
|
||||
)
|
||||
const wrapper = mountComponent(widget, 'option1')
|
||||
|
||||
@@ -187,7 +272,20 @@ describe('WidgetSelectButton Button Selection', () => {
|
||||
|
||||
it('handles missing callback gracefully', async () => {
|
||||
const options = ['option1', 'option2']
|
||||
const widget = createMockWidget('option1', { values: options }, undefined)
|
||||
const widget = createMockWidget<string>(
|
||||
'option1',
|
||||
{},
|
||||
undefined,
|
||||
{
|
||||
name: 'test_selectbutton',
|
||||
type: 'string'
|
||||
},
|
||||
{
|
||||
type: 'SELECTBUTTON',
|
||||
name: 'test_selectbutton',
|
||||
options: { values: options }
|
||||
}
|
||||
)
|
||||
const wrapper = mountComponent(widget, 'option1')
|
||||
|
||||
await clickSelectButton(wrapper, 'option2')
|
||||
@@ -200,7 +298,20 @@ describe('WidgetSelectButton Button Selection', () => {
|
||||
|
||||
it('allows clicking same option again', async () => {
|
||||
const options = ['option1', 'option2']
|
||||
const widget = createMockWidget('option1', { values: options })
|
||||
const widget = createMockWidget<string>(
|
||||
'option1',
|
||||
{},
|
||||
undefined,
|
||||
{
|
||||
name: 'test_selectbutton',
|
||||
type: 'string'
|
||||
},
|
||||
{
|
||||
type: 'SELECTBUTTON',
|
||||
name: 'test_selectbutton',
|
||||
options: { values: options }
|
||||
}
|
||||
)
|
||||
const wrapper = mountComponent(widget, 'option1')
|
||||
|
||||
await clickSelectButton(wrapper, 'option1')
|
||||
@@ -214,7 +325,20 @@ describe('WidgetSelectButton Button Selection', () => {
|
||||
describe('Option Types', () => {
|
||||
it('handles string options', () => {
|
||||
const options = ['apple', 'banana', 'cherry']
|
||||
const widget = createMockWidget('banana', { values: options })
|
||||
const widget = createMockWidget<string>(
|
||||
'banana',
|
||||
{},
|
||||
undefined,
|
||||
{
|
||||
name: 'test_selectbutton',
|
||||
type: 'string'
|
||||
},
|
||||
{
|
||||
type: 'SELECTBUTTON',
|
||||
name: 'test_selectbutton',
|
||||
options: { values: options }
|
||||
}
|
||||
)
|
||||
const wrapper = mountComponent(widget, 'banana')
|
||||
|
||||
const buttons = wrapper.findAll('button')
|
||||
@@ -225,7 +349,20 @@ describe('WidgetSelectButton Button Selection', () => {
|
||||
|
||||
it('handles number options', () => {
|
||||
const options = [1, 2, 3]
|
||||
const widget = createMockWidget('2', { values: options })
|
||||
const widget = createMockWidget<string>(
|
||||
'2',
|
||||
{},
|
||||
undefined,
|
||||
{
|
||||
name: 'test_selectbutton',
|
||||
type: 'string'
|
||||
},
|
||||
{
|
||||
type: 'SELECTBUTTON',
|
||||
name: 'test_selectbutton',
|
||||
options: { values: options }
|
||||
}
|
||||
)
|
||||
const wrapper = mountComponent(widget, '2')
|
||||
|
||||
const buttons = wrapper.findAll('button')
|
||||
@@ -245,7 +382,20 @@ describe('WidgetSelectButton Button Selection', () => {
|
||||
{ label: 'Second Option', value: 'second' },
|
||||
{ label: 'Third Option', value: 'third' }
|
||||
]
|
||||
const widget = createMockWidget('second', { values: options })
|
||||
const widget = createMockWidget<string>(
|
||||
'second',
|
||||
{},
|
||||
undefined,
|
||||
{
|
||||
name: 'test_selectbutton',
|
||||
type: 'string'
|
||||
},
|
||||
{
|
||||
type: 'SELECTBUTTON',
|
||||
name: 'test_selectbutton',
|
||||
options: { values: options }
|
||||
}
|
||||
)
|
||||
const wrapper = mountComponent(widget, 'second')
|
||||
|
||||
const buttons = wrapper.findAll('button')
|
||||
@@ -264,7 +414,20 @@ describe('WidgetSelectButton Button Selection', () => {
|
||||
{ label: 'First', value: 'first_val' },
|
||||
{ label: 'Second', value: 'second_val' }
|
||||
]
|
||||
const widget = createMockWidget('first_val', { values: options })
|
||||
const widget = createMockWidget<string>(
|
||||
'first_val',
|
||||
{},
|
||||
undefined,
|
||||
{
|
||||
name: 'test_selectbutton',
|
||||
type: 'string'
|
||||
},
|
||||
{
|
||||
type: 'SELECTBUTTON',
|
||||
name: 'test_selectbutton',
|
||||
options: { values: options }
|
||||
}
|
||||
)
|
||||
const wrapper = mountComponent(widget, 'first_val')
|
||||
|
||||
await clickSelectButton(wrapper, 'Second')
|
||||
@@ -278,7 +441,20 @@ describe('WidgetSelectButton Button Selection', () => {
|
||||
describe('Edge Cases', () => {
|
||||
it('handles options with special characters', () => {
|
||||
const options = ['@#$%^&*()', '{}[]|\\:";\'<>?,./']
|
||||
const widget = createMockWidget(options[0], { values: options })
|
||||
const widget = createMockWidget<string>(
|
||||
options[0],
|
||||
{},
|
||||
undefined,
|
||||
{
|
||||
name: 'test_selectbutton',
|
||||
type: 'string'
|
||||
},
|
||||
{
|
||||
type: 'SELECTBUTTON',
|
||||
name: 'test_selectbutton',
|
||||
options: { values: options }
|
||||
}
|
||||
)
|
||||
const wrapper = mountComponent(widget, options[0])
|
||||
|
||||
const buttons = wrapper.findAll('button')
|
||||
@@ -288,7 +464,20 @@ describe('WidgetSelectButton Button Selection', () => {
|
||||
|
||||
it('handles empty string options', () => {
|
||||
const options = ['', 'not empty', ' ', 'normal']
|
||||
const widget = createMockWidget('', { values: options })
|
||||
const widget = createMockWidget<string>(
|
||||
'',
|
||||
{},
|
||||
undefined,
|
||||
{
|
||||
name: 'test_selectbutton',
|
||||
type: 'string'
|
||||
},
|
||||
{
|
||||
type: 'SELECTBUTTON',
|
||||
name: 'test_selectbutton',
|
||||
options: { values: options }
|
||||
}
|
||||
)
|
||||
const wrapper = mountComponent(widget, '')
|
||||
|
||||
const buttons = wrapper.findAll('button')
|
||||
@@ -305,7 +494,20 @@ describe('WidgetSelectButton Button Selection', () => {
|
||||
undefined,
|
||||
'another'
|
||||
]
|
||||
const widget = createMockWidget('valid', { values: options })
|
||||
const widget = createMockWidget<string>(
|
||||
'valid',
|
||||
{},
|
||||
undefined,
|
||||
{
|
||||
name: 'test_selectbutton',
|
||||
type: 'string'
|
||||
},
|
||||
{
|
||||
type: 'SELECTBUTTON',
|
||||
name: 'test_selectbutton',
|
||||
options: { values: options }
|
||||
}
|
||||
)
|
||||
const wrapper = mountComponent(widget, 'valid')
|
||||
|
||||
const buttons = wrapper.findAll('button')
|
||||
@@ -319,7 +521,20 @@ describe('WidgetSelectButton Button Selection', () => {
|
||||
const longText =
|
||||
'This is a very long option text that might cause layout issues if not handled properly'
|
||||
const options = ['short', longText, 'normal']
|
||||
const widget = createMockWidget('short', { values: options })
|
||||
const widget = createMockWidget<string>(
|
||||
'short',
|
||||
{},
|
||||
undefined,
|
||||
{
|
||||
name: 'test_selectbutton',
|
||||
type: 'string'
|
||||
},
|
||||
{
|
||||
type: 'SELECTBUTTON',
|
||||
name: 'test_selectbutton',
|
||||
options: { values: options }
|
||||
}
|
||||
)
|
||||
const wrapper = mountComponent(widget, 'short')
|
||||
|
||||
const buttons = wrapper.findAll('button')
|
||||
@@ -328,7 +543,20 @@ describe('WidgetSelectButton Button Selection', () => {
|
||||
|
||||
it('handles large number of options', () => {
|
||||
const options = Array.from({ length: 20 }, (_, i) => `option${i + 1}`)
|
||||
const widget = createMockWidget('option5', { values: options })
|
||||
const widget = createMockWidget<string>(
|
||||
'option5',
|
||||
{},
|
||||
undefined,
|
||||
{
|
||||
name: 'test_selectbutton',
|
||||
type: 'string'
|
||||
},
|
||||
{
|
||||
type: 'SELECTBUTTON',
|
||||
name: 'test_selectbutton',
|
||||
options: { values: options }
|
||||
}
|
||||
)
|
||||
const wrapper = mountComponent(widget, 'option5')
|
||||
|
||||
const buttons = wrapper.findAll('button')
|
||||
@@ -340,7 +568,20 @@ describe('WidgetSelectButton Button Selection', () => {
|
||||
|
||||
it('handles duplicate options', () => {
|
||||
const options = ['duplicate', 'unique', 'duplicate', 'unique']
|
||||
const widget = createMockWidget('duplicate', { values: options })
|
||||
const widget = createMockWidget<string>(
|
||||
'duplicate',
|
||||
{},
|
||||
undefined,
|
||||
{
|
||||
name: 'test_selectbutton',
|
||||
type: 'string'
|
||||
},
|
||||
{
|
||||
type: 'SELECTBUTTON',
|
||||
name: 'test_selectbutton',
|
||||
options: { values: options }
|
||||
}
|
||||
)
|
||||
const wrapper = mountComponent(widget, 'duplicate')
|
||||
|
||||
const buttons = wrapper.findAll('button')
|
||||
@@ -358,7 +599,20 @@ describe('WidgetSelectButton Button Selection', () => {
|
||||
describe('Styling and Layout', () => {
|
||||
it('applies proper button styling', () => {
|
||||
const options = ['option1', 'option2']
|
||||
const widget = createMockWidget('option1', { values: options })
|
||||
const widget = createMockWidget<string>(
|
||||
'option1',
|
||||
{},
|
||||
undefined,
|
||||
{
|
||||
name: 'test_selectbutton',
|
||||
type: 'string'
|
||||
},
|
||||
{
|
||||
type: 'SELECTBUTTON',
|
||||
name: 'test_selectbutton',
|
||||
options: { values: options }
|
||||
}
|
||||
)
|
||||
const wrapper = mountComponent(widget, 'option1')
|
||||
|
||||
const buttons = wrapper.findAll('button')
|
||||
@@ -374,7 +628,20 @@ describe('WidgetSelectButton Button Selection', () => {
|
||||
|
||||
it('applies hover effects for non-selected options', () => {
|
||||
const options = ['option1', 'option2']
|
||||
const widget = createMockWidget('option1', { values: options })
|
||||
const widget = createMockWidget<string>(
|
||||
'option1',
|
||||
{},
|
||||
undefined,
|
||||
{
|
||||
name: 'test_selectbutton',
|
||||
type: 'string'
|
||||
},
|
||||
{
|
||||
type: 'SELECTBUTTON',
|
||||
name: 'test_selectbutton',
|
||||
options: { values: options }
|
||||
}
|
||||
)
|
||||
const wrapper = mountComponent(widget, 'option1', false)
|
||||
|
||||
const buttons = wrapper.findAll('button')
|
||||
@@ -389,7 +656,20 @@ describe('WidgetSelectButton Button Selection', () => {
|
||||
|
||||
describe('Integration with Layout', () => {
|
||||
it('renders within WidgetLayoutField', () => {
|
||||
const widget = createMockWidget('test', { values: ['test'] })
|
||||
const widget = createMockWidget<string>(
|
||||
'test',
|
||||
{},
|
||||
undefined,
|
||||
{
|
||||
name: 'test_selectbutton',
|
||||
type: 'string'
|
||||
},
|
||||
{
|
||||
type: 'SELECTBUTTON',
|
||||
name: 'test_selectbutton',
|
||||
options: { values: ['test'] }
|
||||
}
|
||||
)
|
||||
const wrapper = mountComponent(widget, 'test')
|
||||
|
||||
const layoutField = wrapper.findComponent({ name: 'WidgetLayoutField' })
|
||||
@@ -398,8 +678,20 @@ describe('WidgetSelectButton Button Selection', () => {
|
||||
})
|
||||
|
||||
it('passes widget name to layout field', () => {
|
||||
const widget = createMockWidget('test', { values: ['test'] })
|
||||
widget.name = 'custom_select_button'
|
||||
const widget = createMockWidget<string>(
|
||||
'test',
|
||||
{},
|
||||
undefined,
|
||||
{
|
||||
name: 'custom_select_button',
|
||||
type: 'string'
|
||||
},
|
||||
{
|
||||
type: 'SELECTBUTTON',
|
||||
name: 'custom_select_button',
|
||||
options: { values: ['test'] }
|
||||
}
|
||||
)
|
||||
const wrapper = mountComponent(widget, 'test')
|
||||
|
||||
const layoutField = wrapper.findComponent({ name: 'WidgetLayoutField' })
|
||||
|
||||
@@ -41,6 +41,6 @@ const selectOptions = computed(() => {
|
||||
if (!spec || !isSelectButtonInputSpec(spec)) {
|
||||
return []
|
||||
}
|
||||
return spec.options?.values || []
|
||||
return spec.options?.values ?? []
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -22,6 +22,10 @@ import { useWidgetValue } from '@/composables/graph/useWidgetValue'
|
||||
import { useTransformCompatOverlayProps } from '@/composables/useTransformCompatOverlayProps'
|
||||
import { isTreeSelectInputSpec } from '@/schemas/nodeDef/nodeDefSchemaV2'
|
||||
import type { SimplifiedWidget } from '@/types/simplifiedWidget'
|
||||
import {
|
||||
PANEL_EXCLUDED_PROPS,
|
||||
filterWidgetProps
|
||||
} from '@/utils/widgetPropFilter'
|
||||
|
||||
import WidgetLayoutField from './layout/WidgetLayoutField.vue'
|
||||
|
||||
@@ -54,26 +58,32 @@ const { localValue, onChange } = useWidgetValue({
|
||||
// Transform compatibility props for overlay positioning
|
||||
const transformCompatProps = useTransformCompatOverlayProps()
|
||||
|
||||
// TreeSelect specific excluded props
|
||||
const TREE_SELECT_EXCLUDED_PROPS = [
|
||||
...PANEL_EXCLUDED_PROPS,
|
||||
'inputClass',
|
||||
'inputStyle'
|
||||
] as const
|
||||
|
||||
const combinedProps = computed(() => {
|
||||
const spec = props.widget.spec
|
||||
if (!spec || !isTreeSelectInputSpec(spec)) {
|
||||
return {
|
||||
...props.widget.options,
|
||||
...filterWidgetProps(props.widget.options, TREE_SELECT_EXCLUDED_PROPS),
|
||||
...transformCompatProps.value
|
||||
}
|
||||
}
|
||||
|
||||
const specOptions = spec.options || {}
|
||||
return {
|
||||
// Include runtime props like disabled
|
||||
...props.widget.options,
|
||||
// Include runtime props like disabled, but filter out panel-related ones
|
||||
...filterWidgetProps(props.widget.options, TREE_SELECT_EXCLUDED_PROPS),
|
||||
// PrimeVue TreeSelect expects 'options' to be an array of tree nodes
|
||||
options: (specOptions.values as TreeNode[]) || [],
|
||||
// Convert 'multiple' to PrimeVue's 'selectionMode'
|
||||
selectionMode: (specOptions.multiple ? 'multiple' : 'single') as
|
||||
| 'single'
|
||||
| 'multiple'
|
||||
| 'checkbox',
|
||||
selectionMode: specOptions.multiple
|
||||
? ('multiple' as const)
|
||||
: ('single' as const),
|
||||
// Pass through other props like placeholder
|
||||
placeholder: specOptions.placeholder,
|
||||
...transformCompatProps.value
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
import type { SimplifiedWidget, WidgetValue } from '@/types/simplifiedWidget'
|
||||
import type { InputSpec } from '@/schemas/nodeDef/nodeDefSchemaV2'
|
||||
|
||||
/**
|
||||
* Creates a mock SimplifiedWidget for testing Vue Node widgets.
|
||||
* This utility function is shared across widget component tests to ensure consistency.
|
||||
*/
|
||||
export function createMockWidget<T extends WidgetValue>(
|
||||
export function createMockWidget<T extends WidgetValue = WidgetValue>(
|
||||
value: T = null as T,
|
||||
options: Record<string, any> = {},
|
||||
callback?: (value: T) => void,
|
||||
overrides: Partial<SimplifiedWidget<T>> = {}
|
||||
overrides: Partial<SimplifiedWidget<T>> = {},
|
||||
spec?: Partial<InputSpec>
|
||||
): SimplifiedWidget<T> {
|
||||
return {
|
||||
const widget: SimplifiedWidget<T> = {
|
||||
name: 'test_widget',
|
||||
type: 'default',
|
||||
value,
|
||||
@@ -18,6 +20,13 @@ export function createMockWidget<T extends WidgetValue>(
|
||||
callback,
|
||||
...overrides
|
||||
}
|
||||
|
||||
// Only add spec if provided
|
||||
if (spec) {
|
||||
widget.spec = spec as InputSpec
|
||||
}
|
||||
|
||||
return widget
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -224,6 +224,7 @@ type StringInputSpec = z.infer<typeof zStringInputSpec>
|
||||
export type ComboInputSpec = z.infer<typeof zComboInputSpec>
|
||||
export type ColorInputSpec = z.infer<typeof zColorInputSpec>
|
||||
export type FileUploadInputSpec = z.infer<typeof zFileUploadInputSpec>
|
||||
export type ImageInputSpec = z.infer<typeof zImageInputSpec>
|
||||
export type ImageCompareInputSpec = z.infer<typeof zImageCompareInputSpec>
|
||||
export type TreeSelectInputSpec = z.infer<typeof zTreeSelectInputSpec>
|
||||
export type MultiSelectInputSpec = z.infer<typeof zMultiSelectInputSpec>
|
||||
@@ -308,3 +309,9 @@ export const isFileUploadInputSpec = (
|
||||
): inputSpec is FileUploadInputSpec => {
|
||||
return inputSpec.type === 'FILEUPLOAD'
|
||||
}
|
||||
|
||||
export const isImageInputSpec = (
|
||||
inputSpec: InputSpec
|
||||
): inputSpec is ImageInputSpec => {
|
||||
return inputSpec.type === 'IMAGE'
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user