feat: Add more Storybook stories for UI components

This commit is contained in:
snomiao
2025-09-22 21:10:30 +00:00
parent e5d4d07d32
commit ce71c2c529
521 changed files with 8078 additions and 22384 deletions

View File

@@ -2,10 +2,10 @@ import { createPinia, setActivePinia } from 'pinia'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { useSelectedLiteGraphItems } from '@/composables/canvas/useSelectedLiteGraphItems'
import type { LGraphNode } from '@/lib/litegraph/src/litegraph'
import {
LGraphEventMode,
type Positionable,
LGraphNode,
Positionable,
Reroute
} from '@/lib/litegraph/src/litegraph'
import { useCanvasStore } from '@/renderer/core/canvas/canvasStore'

View File

@@ -0,0 +1,159 @@
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { useCanvasInteractions } from '@/composables/graph/useCanvasInteractions'
import type { LGraphCanvas } from '@/lib/litegraph/src/litegraph'
import { useSettingStore } from '@/platform/settings/settingStore'
import { useCanvasStore } from '@/renderer/core/canvas/canvasStore'
// Mock stores
vi.mock('@/renderer/core/canvas/canvasStore', () => {
const getCanvas = vi.fn()
const setCursorStyle = vi.fn()
return {
useCanvasStore: vi.fn(() => ({
getCanvas,
setCursorStyle
}))
}
})
vi.mock('@/platform/settings/settingStore', () => {
const getFn = vi.fn()
return { useSettingStore: vi.fn(() => ({ get: getFn })) }
})
vi.mock('@/scripts/app', () => ({
app: {
canvas: {
canvas: {
dispatchEvent: vi.fn()
}
}
}
}))
function createMockLGraphCanvas(read_only = true): LGraphCanvas {
const mockCanvas: Partial<LGraphCanvas> = { read_only }
return mockCanvas as LGraphCanvas
}
function createMockPointerEvent(
buttons: PointerEvent['buttons'] = 1
): PointerEvent {
const mockEvent: Partial<PointerEvent> = {
buttons,
preventDefault: vi.fn(),
stopPropagation: vi.fn()
}
return mockEvent as PointerEvent
}
function createMockWheelEvent(ctrlKey = false, metaKey = false): WheelEvent {
const mockEvent: Partial<WheelEvent> = {
ctrlKey,
metaKey,
preventDefault: vi.fn(),
stopPropagation: vi.fn()
}
return mockEvent as WheelEvent
}
describe('useCanvasInteractions', () => {
beforeEach(() => {
vi.resetAllMocks()
})
describe('handlePointer', () => {
it('should intercept left mouse events when canvas is read_only to enable space+drag navigation', () => {
const { getCanvas } = useCanvasStore()
const mockCanvas = createMockLGraphCanvas(true)
vi.mocked(getCanvas).mockReturnValue(mockCanvas)
const { handlePointer } = useCanvasInteractions()
const mockEvent = createMockPointerEvent(1) // Left Mouse Button
handlePointer(mockEvent)
expect(mockEvent.preventDefault).toHaveBeenCalled()
expect(mockEvent.stopPropagation).toHaveBeenCalled()
})
it('should forward middle mouse button events to canvas', () => {
const { getCanvas } = useCanvasStore()
const mockCanvas = createMockLGraphCanvas(false)
vi.mocked(getCanvas).mockReturnValue(mockCanvas)
const { handlePointer } = useCanvasInteractions()
const mockEvent = createMockPointerEvent(4) // Middle mouse button
handlePointer(mockEvent)
expect(mockEvent.preventDefault).toHaveBeenCalled()
expect(mockEvent.stopPropagation).toHaveBeenCalled()
})
it('should not prevent default when canvas is not in read_only mode and not middle button', () => {
const { getCanvas } = useCanvasStore()
const mockCanvas = createMockLGraphCanvas(false)
vi.mocked(getCanvas).mockReturnValue(mockCanvas)
const { handlePointer } = useCanvasInteractions()
const mockEvent = createMockPointerEvent(1)
handlePointer(mockEvent)
expect(mockEvent.preventDefault).not.toHaveBeenCalled()
expect(mockEvent.stopPropagation).not.toHaveBeenCalled()
})
it('should return early when canvas is null', () => {
const { getCanvas } = useCanvasStore()
vi.mocked(getCanvas).mockReturnValue(null as unknown as LGraphCanvas) // TODO: Fix misaligned types
const { handlePointer } = useCanvasInteractions()
const mockEvent = createMockPointerEvent(1)
handlePointer(mockEvent)
expect(getCanvas).toHaveBeenCalled()
expect(mockEvent.preventDefault).not.toHaveBeenCalled()
expect(mockEvent.stopPropagation).not.toHaveBeenCalled()
})
})
describe('handleWheel', () => {
it('should forward ctrl+wheel events to canvas in standard nav mode', () => {
const { get } = useSettingStore()
vi.mocked(get).mockReturnValue('standard')
const { handleWheel } = useCanvasInteractions()
// Ctrl key pressed
const mockEvent = createMockWheelEvent(true)
handleWheel(mockEvent)
expect(mockEvent.preventDefault).toHaveBeenCalled()
expect(mockEvent.stopPropagation).toHaveBeenCalled()
})
it('should forward all wheel events to canvas in legacy nav mode', () => {
const { get } = useSettingStore()
vi.mocked(get).mockReturnValue('legacy')
const { handleWheel } = useCanvasInteractions()
const mockEvent = createMockWheelEvent()
handleWheel(mockEvent)
expect(mockEvent.preventDefault).toHaveBeenCalled()
expect(mockEvent.stopPropagation).toHaveBeenCalled()
})
it('should not prevent default for regular wheel events in standard nav mode', () => {
const { get } = useSettingStore()
vi.mocked(get).mockReturnValue('standard')
const { handleWheel } = useCanvasInteractions()
const mockEvent = createMockWheelEvent()
handleWheel(mockEvent)
expect(mockEvent.preventDefault).not.toHaveBeenCalled()
expect(mockEvent.stopPropagation).not.toHaveBeenCalled()
})
})
})

View File

@@ -1894,159 +1894,4 @@ describe('useNodePricing', () => {
expect(getNodeDisplayPrice(missingDuration)).toBe('Token-based')
})
})
describe('dynamic pricing - WanTextToVideoApi', () => {
it('should return $1.50 for 10s at 1080p', () => {
const { getNodeDisplayPrice } = useNodePricing()
const node = createMockNode('WanTextToVideoApi', [
{ name: 'duration', value: '10' },
{ name: 'size', value: '1080p: 4:3 (1632x1248)' }
])
const price = getNodeDisplayPrice(node)
expect(price).toBe('$1.50/Run') // 0.15 * 10
})
it('should return $0.50 for 5s at 720p', () => {
const { getNodeDisplayPrice } = useNodePricing()
const node = createMockNode('WanTextToVideoApi', [
{ name: 'duration', value: 5 },
{ name: 'size', value: '720p: 16:9 (1280x720)' }
])
const price = getNodeDisplayPrice(node)
expect(price).toBe('$0.50/Run') // 0.10 * 5
})
it('should return $0.15 for 3s at 480p', () => {
const { getNodeDisplayPrice } = useNodePricing()
const node = createMockNode('WanTextToVideoApi', [
{ name: 'duration', value: '3' },
{ name: 'size', value: '480p: 1:1 (624x624)' }
])
const price = getNodeDisplayPrice(node)
expect(price).toBe('$0.15/Run') // 0.05 * 3
})
it('should fall back when widgets are missing', () => {
const { getNodeDisplayPrice } = useNodePricing()
const missingBoth = createMockNode('WanTextToVideoApi', [])
const missingSize = createMockNode('WanTextToVideoApi', [
{ name: 'duration', value: '5' }
])
const missingDuration = createMockNode('WanTextToVideoApi', [
{ name: 'size', value: '1080p' }
])
expect(getNodeDisplayPrice(missingBoth)).toBe('$0.05-0.15/second')
expect(getNodeDisplayPrice(missingSize)).toBe('$0.05-0.15/second')
expect(getNodeDisplayPrice(missingDuration)).toBe('$0.05-0.15/second')
})
it('should fall back on invalid duration', () => {
const { getNodeDisplayPrice } = useNodePricing()
const node = createMockNode('WanTextToVideoApi', [
{ name: 'duration', value: 'invalid' },
{ name: 'size', value: '1080p' }
])
const price = getNodeDisplayPrice(node)
expect(price).toBe('$0.05-0.15/second')
})
it('should fall back on unknown resolution', () => {
const { getNodeDisplayPrice } = useNodePricing()
const node = createMockNode('WanTextToVideoApi', [
{ name: 'duration', value: '10' },
{ name: 'size', value: '2K' }
])
const price = getNodeDisplayPrice(node)
expect(price).toBe('$0.05-0.15/second')
})
})
describe('dynamic pricing - WanImageToVideoApi', () => {
it('should return $0.80 for 8s at 720p', () => {
const { getNodeDisplayPrice } = useNodePricing()
const node = createMockNode('WanImageToVideoApi', [
{ name: 'duration', value: 8 },
{ name: 'resolution', value: '720p' }
])
const price = getNodeDisplayPrice(node)
expect(price).toBe('$0.80/Run') // 0.10 * 8
})
it('should return $0.60 for 12s at 480P', () => {
const { getNodeDisplayPrice } = useNodePricing()
const node = createMockNode('WanImageToVideoApi', [
{ name: 'duration', value: '12' },
{ name: 'resolution', value: '480P' }
])
const price = getNodeDisplayPrice(node)
expect(price).toBe('$0.60/Run') // 0.05 * 12
})
it('should return $1.50 for 10s at 1080p', () => {
const { getNodeDisplayPrice } = useNodePricing()
const node = createMockNode('WanImageToVideoApi', [
{ name: 'duration', value: '10' },
{ name: 'resolution', value: '1080p' }
])
const price = getNodeDisplayPrice(node)
expect(price).toBe('$1.50/Run') // 0.15 * 10
})
it('should handle "5s" string duration at 1080P', () => {
const { getNodeDisplayPrice } = useNodePricing()
const node = createMockNode('WanImageToVideoApi', [
{ name: 'duration', value: '5s' },
{ name: 'resolution', value: '1080P' }
])
const price = getNodeDisplayPrice(node)
expect(price).toBe('$0.75/Run') // 0.15 * 5
})
it('should fall back when widgets are missing', () => {
const { getNodeDisplayPrice } = useNodePricing()
const missingBoth = createMockNode('WanImageToVideoApi', [])
const missingRes = createMockNode('WanImageToVideoApi', [
{ name: 'duration', value: '5' }
])
const missingDuration = createMockNode('WanImageToVideoApi', [
{ name: 'resolution', value: '1080p' }
])
expect(getNodeDisplayPrice(missingBoth)).toBe('$0.05-0.15/second')
expect(getNodeDisplayPrice(missingRes)).toBe('$0.05-0.15/second')
expect(getNodeDisplayPrice(missingDuration)).toBe('$0.05-0.15/second')
})
it('should fall back on invalid duration', () => {
const { getNodeDisplayPrice } = useNodePricing()
const node = createMockNode('WanImageToVideoApi', [
{ name: 'duration', value: 'invalid' },
{ name: 'resolution', value: '720p' }
])
const price = getNodeDisplayPrice(node)
expect(price).toBe('$0.05-0.15/second')
})
it('should fall back on unknown resolution', () => {
const { getNodeDisplayPrice } = useNodePricing()
const node = createMockNode('WanImageToVideoApi', [
{ name: 'duration', value: '10' },
{ name: 'resolution', value: 'weird-res' }
])
const price = getNodeDisplayPrice(node)
expect(price).toBe('$0.05-0.15/second')
})
})
})

View File

@@ -3,8 +3,8 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { ref } from 'vue'
import { usePacksSelection } from '@/composables/nodePack/usePacksSelection'
import { useComfyManagerStore } from '@/stores/comfyManagerStore'
import type { components } from '@/types/comfyRegistryTypes'
import { useComfyManagerStore } from '@/workbench/extensions/manager/stores/comfyManagerStore'
vi.mock('vue-i18n', async () => {
const actual = await vi.importActual('vue-i18n')

View File

@@ -4,7 +4,7 @@ import { nextTick } from 'vue'
import { useConflictDetection } from '@/composables/useConflictDetection'
import type { components } from '@/types/comfyRegistryTypes'
import type { components as ManagerComponents } from '@/workbench/extensions/manager/types/generatedManagerTypes'
import type { components as ManagerComponents } from '@/types/generatedManagerTypes'
type InstalledPacksResponse =
ManagerComponents['schemas']['InstalledPacksResponse']
@@ -27,7 +27,7 @@ vi.mock('@/scripts/api', () => ({
}
}))
vi.mock('@/workbench/extensions/manager/services/comfyManagerService', () => ({
vi.mock('@/services/comfyManagerService', () => ({
useComfyManagerService: vi.fn()
}))
@@ -57,7 +57,7 @@ vi.mock('@/composables/nodePack/useInstalledPacks', () => ({
}))
}))
vi.mock('@/workbench/extensions/manager/stores/comfyManagerStore', () => ({
vi.mock('@/stores/comfyManagerStore', () => ({
useComfyManagerStore: vi.fn(() => ({
isPackInstalled: vi.fn(),
installedPacks: { value: [] }
@@ -140,7 +140,7 @@ describe.skip('useConflictDetection with Registry Store', () => {
// Mock useComfyManagerService
const { useComfyManagerService } = await import(
'@/workbench/extensions/manager/services/comfyManagerService'
'@/services/comfyManagerService'
)
vi.mocked(useComfyManagerService).mockReturnValue(
mockComfyManagerService as any

View File

@@ -4,11 +4,11 @@ import { computed, ref } from 'vue'
import { useImportFailedDetection } from '@/composables/useImportFailedDetection'
import * as dialogService from '@/services/dialogService'
import * as comfyManagerStore from '@/stores/comfyManagerStore'
import * as conflictDetectionStore from '@/stores/conflictDetectionStore'
import * as comfyManagerStore from '@/workbench/extensions/manager/stores/comfyManagerStore'
// Mock the stores and services
vi.mock('@/workbench/extensions/manager/stores/comfyManagerStore')
vi.mock('@/stores/comfyManagerStore')
vi.mock('@/stores/conflictDetectionStore')
vi.mock('@/services/dialogService')
vi.mock('vue-i18n', async (importOriginal) => {

View File

@@ -1,8 +1,8 @@
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { ref } from 'vue'
import { useManagerQueue } from '@/workbench/extensions/manager/composables/useManagerQueue'
import type { components } from '@/workbench/extensions/manager/types/generatedManagerTypes'
import { useManagerQueue } from '@/composables/useManagerQueue'
import { components } from '@/types/generatedManagerTypes'
// Mock dialog service
vi.mock('@/services/dialogService', () => ({

View File

@@ -2,13 +2,10 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'
import { ref } from 'vue'
import { useFeatureFlags } from '@/composables/useFeatureFlags'
import { ManagerUIState, useManagerState } from '@/composables/useManagerState'
import { api } from '@/scripts/api'
import { useExtensionStore } from '@/stores/extensionStore'
import { useSystemStatsStore } from '@/stores/systemStatsStore'
import {
ManagerUIState,
useManagerState
} from '@/workbench/extensions/manager/composables/useManagerState'
// Mock dependencies
vi.mock('@/scripts/api', () => ({
@@ -59,10 +56,10 @@ describe('useManagerState', () => {
})
describe('managerUIState property', () => {
it('should return DISABLED state when --enable-manager is NOT present', () => {
it('should return DISABLED state when --disable-manager is present', () => {
vi.mocked(useSystemStatsStore).mockReturnValue({
systemStats: ref({
system: { argv: ['python', 'main.py'] } // No --enable-manager flag
system: { argv: ['python', 'main.py', '--disable-manager'] }
}),
isInitialized: ref(true)
} as any)
@@ -79,14 +76,7 @@ describe('useManagerState', () => {
it('should return LEGACY_UI state when --enable-manager-legacy-ui is present', () => {
vi.mocked(useSystemStatsStore).mockReturnValue({
systemStats: ref({
system: {
argv: [
'python',
'main.py',
'--enable-manager',
'--enable-manager-legacy-ui'
]
} // Both flags needed
system: { argv: ['python', 'main.py', '--enable-manager-legacy-ui'] }
}),
isInitialized: ref(true)
} as any)
@@ -102,9 +92,7 @@ describe('useManagerState', () => {
it('should return NEW_UI state when client and server both support v4', () => {
vi.mocked(useSystemStatsStore).mockReturnValue({
systemStats: ref({
system: { argv: ['python', 'main.py', '--enable-manager'] }
}), // Need --enable-manager
systemStats: ref({ system: { argv: ['python', 'main.py'] } }),
isInitialized: ref(true)
} as any)
vi.mocked(api.getClientFeatureFlags).mockReturnValue({
@@ -126,9 +114,7 @@ describe('useManagerState', () => {
it('should return LEGACY_UI state when server supports v4 but client does not', () => {
vi.mocked(useSystemStatsStore).mockReturnValue({
systemStats: ref({
system: { argv: ['python', 'main.py', '--enable-manager'] }
}), // Need --enable-manager
systemStats: ref({ system: { argv: ['python', 'main.py'] } }),
isInitialized: ref(true)
} as any)
vi.mocked(api.getClientFeatureFlags).mockReturnValue({
@@ -150,9 +136,7 @@ describe('useManagerState', () => {
it('should return LEGACY_UI state when legacy manager extension exists', () => {
vi.mocked(useSystemStatsStore).mockReturnValue({
systemStats: ref({
system: { argv: ['python', 'main.py', '--enable-manager'] }
}), // Need --enable-manager
systemStats: ref({ system: { argv: ['python', 'main.py'] } }),
isInitialized: ref(true)
} as any)
vi.mocked(api.getClientFeatureFlags).mockReturnValue({})
@@ -171,9 +155,7 @@ describe('useManagerState', () => {
it('should return NEW_UI state when server feature flags are undefined', () => {
vi.mocked(useSystemStatsStore).mockReturnValue({
systemStats: ref({
system: { argv: ['python', 'main.py', '--enable-manager'] }
}), // Need --enable-manager
systemStats: ref({ system: { argv: ['python', 'main.py'] } }),
isInitialized: ref(true)
} as any)
vi.mocked(api.getClientFeatureFlags).mockReturnValue({})
@@ -193,9 +175,7 @@ describe('useManagerState', () => {
it('should return LEGACY_UI state when server does not support v4', () => {
vi.mocked(useSystemStatsStore).mockReturnValue({
systemStats: ref({
system: { argv: ['python', 'main.py', '--enable-manager'] }
}), // Need --enable-manager
systemStats: ref({ system: { argv: ['python', 'main.py'] } }),
isInitialized: ref(true)
} as any)
vi.mocked(api.getClientFeatureFlags).mockReturnValue({})
@@ -232,17 +212,14 @@ describe('useManagerState', () => {
const managerState = useManagerState()
// When systemStats is null, we can't check for --enable-manager flag, so manager is disabled
expect(managerState.managerUIState.value).toBe(ManagerUIState.DISABLED)
expect(managerState.managerUIState.value).toBe(ManagerUIState.NEW_UI)
})
})
describe('helper properties', () => {
it('isManagerEnabled should return true when state is not DISABLED', () => {
vi.mocked(useSystemStatsStore).mockReturnValue({
systemStats: ref({
system: { argv: ['python', 'main.py', '--enable-manager'] }
}), // Need --enable-manager
systemStats: ref({ system: { argv: ['python', 'main.py'] } }),
isInitialized: ref(true)
} as any)
vi.mocked(api.getClientFeatureFlags).mockReturnValue({
@@ -260,7 +237,7 @@ describe('useManagerState', () => {
it('isManagerEnabled should return false when state is DISABLED', () => {
vi.mocked(useSystemStatsStore).mockReturnValue({
systemStats: ref({
system: { argv: ['python', 'main.py'] } // No --enable-manager flag means disabled
system: { argv: ['python', 'main.py', '--disable-manager'] }
}),
isInitialized: ref(true)
} as any)
@@ -275,9 +252,7 @@ describe('useManagerState', () => {
it('isNewManagerUI should return true when state is NEW_UI', () => {
vi.mocked(useSystemStatsStore).mockReturnValue({
systemStats: ref({
system: { argv: ['python', 'main.py', '--enable-manager'] }
}), // Need --enable-manager
systemStats: ref({ system: { argv: ['python', 'main.py'] } }),
isInitialized: ref(true)
} as any)
vi.mocked(api.getClientFeatureFlags).mockReturnValue({
@@ -295,14 +270,7 @@ describe('useManagerState', () => {
it('isLegacyManagerUI should return true when state is LEGACY_UI', () => {
vi.mocked(useSystemStatsStore).mockReturnValue({
systemStats: ref({
system: {
argv: [
'python',
'main.py',
'--enable-manager',
'--enable-manager-legacy-ui'
]
} // Both flags needed
system: { argv: ['python', 'main.py', '--enable-manager-legacy-ui'] }
}),
isInitialized: ref(true)
} as any)
@@ -317,9 +285,7 @@ describe('useManagerState', () => {
it('shouldShowInstallButton should return true only for NEW_UI', () => {
vi.mocked(useSystemStatsStore).mockReturnValue({
systemStats: ref({
system: { argv: ['python', 'main.py', '--enable-manager'] }
}), // Need --enable-manager
systemStats: ref({ system: { argv: ['python', 'main.py'] } }),
isInitialized: ref(true)
} as any)
vi.mocked(api.getClientFeatureFlags).mockReturnValue({
@@ -336,9 +302,7 @@ describe('useManagerState', () => {
it('shouldShowManagerButtons should return true when not DISABLED', () => {
vi.mocked(useSystemStatsStore).mockReturnValue({
systemStats: ref({
system: { argv: ['python', 'main.py', '--enable-manager'] }
}), // Need --enable-manager
systemStats: ref({ system: { argv: ['python', 'main.py'] } }),
isInitialized: ref(true)
} as any)
vi.mocked(api.getClientFeatureFlags).mockReturnValue({

View File

@@ -5,9 +5,9 @@ import { useMissingNodes } from '@/composables/nodePack/useMissingNodes'
import { useWorkflowPacks } from '@/composables/nodePack/useWorkflowPacks'
import type { LGraphNode } from '@/lib/litegraph/src/litegraph'
import { app } from '@/scripts/app'
import { useComfyManagerStore } from '@/stores/comfyManagerStore'
import { useNodeDefStore } from '@/stores/nodeDefStore'
import { collectAllNodes } from '@/utils/graphTraversalUtil'
import { useComfyManagerStore } from '@/workbench/extensions/manager/stores/comfyManagerStore'
// Mock Vue's onMounted to execute immediately for testing
vi.mock('vue', async () => {
@@ -23,7 +23,7 @@ vi.mock('@/composables/nodePack/useWorkflowPacks', () => ({
useWorkflowPacks: vi.fn()
}))
vi.mock('@/workbench/extensions/manager/stores/comfyManagerStore', () => ({
vi.mock('@/stores/comfyManagerStore', () => ({
useComfyManagerStore: vi.fn()
}))

View File

@@ -1,7 +1,7 @@
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { useNodeChatHistory } from '@/composables/node/useNodeChatHistory'
import type { LGraphNode } from '@/lib/litegraph/src/litegraph'
import { LGraphNode } from '@/lib/litegraph/src/litegraph'
vi.mock(
'@/renderer/extensions/vueNodes/widgets/composables/useChatHistoryWidget',

View File

@@ -3,7 +3,7 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'
import { nextTick } from 'vue'
import { useServerLogs } from '@/composables/useServerLogs'
import type { LogsWsMessage } from '@/schemas/apiSchema'
import { LogsWsMessage } from '@/schemas/apiSchema'
import { api } from '@/scripts/api'
vi.mock('@/scripts/api', () => ({

View File

@@ -1,10 +1,11 @@
import { compare, valid } from 'semver'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { nextTick, ref } from 'vue'
import { useInstalledPacks } from '@/composables/nodePack/useInstalledPacks'
import { useUpdateAvailableNodes } from '@/composables/nodePack/useUpdateAvailableNodes'
import { useComfyManagerStore } from '@/workbench/extensions/manager/stores/comfyManagerStore'
import { useComfyManagerStore } from '@/stores/comfyManagerStore'
// Import mocked utils
import { compareVersions, isSemVer } from '@/utils/formatUtil'
// Mock Vue's onMounted to execute immediately for testing
vi.mock('vue', async () => {
@@ -20,20 +21,20 @@ vi.mock('@/composables/nodePack/useInstalledPacks', () => ({
useInstalledPacks: vi.fn()
}))
vi.mock('@/workbench/extensions/manager/stores/comfyManagerStore', () => ({
vi.mock('@/stores/comfyManagerStore', () => ({
useComfyManagerStore: vi.fn()
}))
vi.mock('semver', () => ({
compare: vi.fn(),
valid: vi.fn()
vi.mock('@/utils/formatUtil', () => ({
compareVersions: vi.fn(),
isSemVer: vi.fn()
}))
const mockUseInstalledPacks = vi.mocked(useInstalledPacks)
const mockUseComfyManagerStore = vi.mocked(useComfyManagerStore)
const mockSemverCompare = vi.mocked(compare)
const mockSemverValid = vi.mocked(valid)
const mockCompareVersions = vi.mocked(compareVersions)
const mockIsSemVer = vi.mocked(isSemVer)
describe('useUpdateAvailableNodes', () => {
const mockInstalledPacks = [
@@ -85,19 +86,19 @@ describe('useUpdateAvailableNodes', () => {
}
})
mockSemverValid.mockImplementation((version) => {
return version &&
typeof version === 'string' &&
!version.includes('nightly')
? version
: null
})
mockIsSemVer.mockImplementation(
(version: string): version is `${number}.${number}.${number}` => {
return !version.includes('nightly')
}
)
mockSemverCompare.mockImplementation((latest, installed) => {
if (latest === '2.0.0' && installed === '1.0.0') return 1 // outdated
if (latest === '1.0.0' && installed === '1.0.0') return 0 // up to date
return 0
})
mockCompareVersions.mockImplementation(
(latest: string | undefined, installed: string | undefined) => {
if (latest === '2.0.0' && installed === '1.0.0') return 1 // outdated
if (latest === '1.0.0' && installed === '1.0.0') return 0 // up to date
return 0
}
)
mockUseComfyManagerStore.mockReturnValue({
isPackInstalled: mockIsPackInstalled,
@@ -321,10 +322,10 @@ describe('useUpdateAvailableNodes', () => {
// Access the computed to trigger the logic
expect(updateAvailableNodePacks.value).toBeDefined()
expect(mockSemverCompare).toHaveBeenCalledWith('2.0.0', '1.0.0')
expect(mockCompareVersions).toHaveBeenCalledWith('2.0.0', '1.0.0')
})
it('calls semver.valid to check nightly versions', () => {
it('calls isSemVer to check nightly versions', () => {
mockUseInstalledPacks.mockReturnValue({
installedPacks: ref([mockInstalledPacks[2]]), // pack-3: nightly
isLoading: ref(false),
@@ -337,7 +338,7 @@ describe('useUpdateAvailableNodes', () => {
// Access the computed to trigger the logic
expect(updateAvailableNodePacks.value).toBeDefined()
expect(mockSemverValid).toHaveBeenCalledWith('nightly-abc123')
expect(mockIsSemVer).toHaveBeenCalledWith('nightly-abc123')
})
it('calls isPackInstalled for each pack', () => {