Files
ComfyUI_frontend/tests-ui/tests/renderer/extensions/vueNodes/lod/useLOD.test.ts
Christian Byrne bfcbcf4873 [refactor] Reorganize Vue nodes to domain-driven design architecture (#5085)
* refactor: Reorganize Vue nodes system to domain-driven design architecture

Move Vue nodes code from scattered technical layers to domain-focused structure:

- Widget system → src/renderer/extensions/vueNodes/widgets/
- LOD optimization → src/renderer/extensions/vueNodes/lod/
- Layout logic → src/renderer/extensions/vueNodes/layout/
- Node components → src/renderer/extensions/vueNodes/components/
- Test structure mirrors source organization

Benefits:
- Clear domain boundaries instead of technical layers
- Everything Vue nodes related in renderer domain (not workbench)
- camelCase naming (vueNodes vs vue-nodes)
- Tests co-located with source domains
- All imports updated to new DDD structure

* fix: Skip spatial index performance test on CI to avoid flaky timing

Performance tests are inherently flaky on CI due to variable system
performance. This test should only run locally like the other
performance tests.
2025-08-29 13:59:48 -04:00

271 lines
8.0 KiB
TypeScript

import { describe, expect, it } from 'vitest'
import { ref } from 'vue'
import {
LODLevel,
LOD_THRESHOLDS,
supportsFeatureAtZoom,
useLOD
} from '@/renderer/extensions/vueNodes/lod/useLOD'
describe('useLOD', () => {
describe('LOD level detection', () => {
it('should return MINIMAL for zoom <= 0.4', () => {
const zoomRef = ref(0.4)
const { lodLevel } = useLOD(zoomRef)
expect(lodLevel.value).toBe(LODLevel.MINIMAL)
zoomRef.value = 0.2
expect(lodLevel.value).toBe(LODLevel.MINIMAL)
zoomRef.value = 0.1
expect(lodLevel.value).toBe(LODLevel.MINIMAL)
})
it('should return REDUCED for 0.4 < zoom <= 0.8', () => {
const zoomRef = ref(0.5)
const { lodLevel } = useLOD(zoomRef)
expect(lodLevel.value).toBe(LODLevel.REDUCED)
zoomRef.value = 0.6
expect(lodLevel.value).toBe(LODLevel.REDUCED)
zoomRef.value = 0.8
expect(lodLevel.value).toBe(LODLevel.REDUCED)
})
it('should return FULL for zoom > 0.8', () => {
const zoomRef = ref(0.9)
const { lodLevel } = useLOD(zoomRef)
expect(lodLevel.value).toBe(LODLevel.FULL)
zoomRef.value = 1.0
expect(lodLevel.value).toBe(LODLevel.FULL)
zoomRef.value = 2.5
expect(lodLevel.value).toBe(LODLevel.FULL)
})
it('should be reactive to zoom changes', () => {
const zoomRef = ref(0.2)
const { lodLevel } = useLOD(zoomRef)
expect(lodLevel.value).toBe(LODLevel.MINIMAL)
zoomRef.value = 0.6
expect(lodLevel.value).toBe(LODLevel.REDUCED)
zoomRef.value = 1.0
expect(lodLevel.value).toBe(LODLevel.FULL)
})
})
describe('rendering decisions', () => {
it('should disable all rendering for MINIMAL LOD', () => {
const zoomRef = ref(0.2)
const {
shouldRenderWidgets,
shouldRenderSlots,
shouldRenderContent,
shouldRenderSlotLabels,
shouldRenderWidgetLabels
} = useLOD(zoomRef)
expect(shouldRenderWidgets.value).toBe(false)
expect(shouldRenderSlots.value).toBe(false)
expect(shouldRenderContent.value).toBe(false)
expect(shouldRenderSlotLabels.value).toBe(false)
expect(shouldRenderWidgetLabels.value).toBe(false)
})
it('should enable widgets/slots but disable labels for REDUCED LOD', () => {
const zoomRef = ref(0.6)
const {
shouldRenderWidgets,
shouldRenderSlots,
shouldRenderContent,
shouldRenderSlotLabels,
shouldRenderWidgetLabels
} = useLOD(zoomRef)
expect(shouldRenderWidgets.value).toBe(true)
expect(shouldRenderSlots.value).toBe(true)
expect(shouldRenderContent.value).toBe(false)
expect(shouldRenderSlotLabels.value).toBe(false)
expect(shouldRenderWidgetLabels.value).toBe(false)
})
it('should enable all rendering for FULL LOD', () => {
const zoomRef = ref(1.0)
const {
shouldRenderWidgets,
shouldRenderSlots,
shouldRenderContent,
shouldRenderSlotLabels,
shouldRenderWidgetLabels
} = useLOD(zoomRef)
expect(shouldRenderWidgets.value).toBe(true)
expect(shouldRenderSlots.value).toBe(true)
expect(shouldRenderContent.value).toBe(true)
expect(shouldRenderSlotLabels.value).toBe(true)
expect(shouldRenderWidgetLabels.value).toBe(true)
})
})
describe('CSS classes', () => {
it('should return correct CSS class for each LOD level', () => {
const zoomRef = ref(0.2)
const { lodCssClass } = useLOD(zoomRef)
expect(lodCssClass.value).toBe('lg-node--lod-minimal')
zoomRef.value = 0.6
expect(lodCssClass.value).toBe('lg-node--lod-reduced')
zoomRef.value = 1.0
expect(lodCssClass.value).toBe('lg-node--lod-full')
})
})
describe('essential widgets filtering', () => {
it('should return all widgets for FULL LOD', () => {
const zoomRef = ref(1.0)
const { getEssentialWidgets } = useLOD(zoomRef)
const widgets = [
{ type: 'combo' },
{ type: 'text' },
{ type: 'button' },
{ type: 'slider' }
]
expect(getEssentialWidgets(widgets)).toEqual(widgets)
})
it('should return empty array for MINIMAL LOD', () => {
const zoomRef = ref(0.2)
const { getEssentialWidgets } = useLOD(zoomRef)
const widgets = [{ type: 'combo' }, { type: 'text' }, { type: 'button' }]
expect(getEssentialWidgets(widgets)).toEqual([])
})
it('should filter to essential types for REDUCED LOD', () => {
const zoomRef = ref(0.6)
const { getEssentialWidgets } = useLOD(zoomRef)
const widgets = [
{ type: 'combo' },
{ type: 'text' },
{ type: 'button' },
{ type: 'slider' },
{ type: 'toggle' },
{ type: 'number' }
]
const essential = getEssentialWidgets(widgets)
expect(essential).toHaveLength(4)
expect(essential.map((w: any) => w.type)).toEqual([
'combo',
'slider',
'toggle',
'number'
])
})
it('should handle case-insensitive widget types', () => {
const zoomRef = ref(0.6)
const { getEssentialWidgets } = useLOD(zoomRef)
const widgets = [
{ type: 'COMBO' },
{ type: 'Select' },
{ type: 'TOGGLE' }
]
const essential = getEssentialWidgets(widgets)
expect(essential).toHaveLength(3)
})
it('should handle widgets with undefined or missing type', () => {
const zoomRef = ref(0.6)
const { getEssentialWidgets } = useLOD(zoomRef)
const widgets = [
{ type: 'combo' },
{ type: undefined },
{},
{ type: 'slider' }
]
const essential = getEssentialWidgets(widgets)
expect(essential).toHaveLength(2)
expect(essential.map((w: any) => w.type)).toEqual(['combo', 'slider'])
})
})
describe('performance metrics', () => {
it('should provide debug metrics', () => {
const zoomRef = ref(0.6)
const { lodMetrics } = useLOD(zoomRef)
expect(lodMetrics.value).toEqual({
level: LODLevel.REDUCED,
zoom: 0.6,
widgetCount: 'full',
slotCount: 'full'
})
})
it('should update metrics when zoom changes', () => {
const zoomRef = ref(0.2)
const { lodMetrics } = useLOD(zoomRef)
expect(lodMetrics.value.level).toBe(LODLevel.MINIMAL)
expect(lodMetrics.value.widgetCount).toBe('none')
expect(lodMetrics.value.slotCount).toBe('none')
zoomRef.value = 1.0
expect(lodMetrics.value.level).toBe(LODLevel.FULL)
expect(lodMetrics.value.widgetCount).toBe('full')
expect(lodMetrics.value.slotCount).toBe('full')
})
})
})
describe('LOD_THRESHOLDS', () => {
it('should export correct threshold values', () => {
expect(LOD_THRESHOLDS.FULL_THRESHOLD).toBe(0.8)
expect(LOD_THRESHOLDS.REDUCED_THRESHOLD).toBe(0.4)
expect(LOD_THRESHOLDS.MINIMAL_THRESHOLD).toBe(0.0)
})
})
describe('supportsFeatureAtZoom', () => {
it('should return correct feature support for different zoom levels', () => {
expect(supportsFeatureAtZoom(1.0, 'renderWidgets')).toBe(true)
expect(supportsFeatureAtZoom(1.0, 'renderSlots')).toBe(true)
expect(supportsFeatureAtZoom(1.0, 'renderContent')).toBe(true)
expect(supportsFeatureAtZoom(0.6, 'renderWidgets')).toBe(true)
expect(supportsFeatureAtZoom(0.6, 'renderSlots')).toBe(true)
expect(supportsFeatureAtZoom(0.6, 'renderContent')).toBe(false)
expect(supportsFeatureAtZoom(0.2, 'renderWidgets')).toBe(false)
expect(supportsFeatureAtZoom(0.2, 'renderSlots')).toBe(false)
expect(supportsFeatureAtZoom(0.2, 'renderContent')).toBe(false)
})
it('should handle threshold boundary values correctly', () => {
expect(supportsFeatureAtZoom(0.8, 'renderWidgets')).toBe(true)
expect(supportsFeatureAtZoom(0.8, 'renderContent')).toBe(false)
expect(supportsFeatureAtZoom(0.81, 'renderContent')).toBe(true)
expect(supportsFeatureAtZoom(0.4, 'renderWidgets')).toBe(false)
expect(supportsFeatureAtZoom(0.41, 'renderWidgets')).toBe(true)
})
})