mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-02 22:37:32 +00:00
Road to No explicit any: Group 8 (part 7) test files (#8459)
## Summary
This PR removes unsafe type assertions ("as unknown as Type") from test
files and improves type safety across the codebase.
### Key Changes
#### Type Safety Improvements
- Removed improper `as unknown as Type` patterns from 17 test files in
Group 8 part 7
- Replaced with proper TypeScript patterns using factory functions and
Mock types
- Fixed createTestingPinia usage in test files (was incorrectly using
createPinia)
- Fixed vi.hoisted pattern for mockSetDirty in viewport tests
- Fixed vi.doMock lint issues with vi.mock and vi.hoisted pattern
- Retained necessary `as unknown as` casts only for complex mock objects
where direct type assertions would fail
### Files Changed
Test files (Group 8 part 7 - services, stores, utils):
- src/services/nodeOrganizationService.test.ts
- src/services/providers/algoliaSearchProvider.test.ts
- src/services/providers/registrySearchProvider.test.ts
- src/stores/comfyRegistryStore.test.ts
- src/stores/domWidgetStore.test.ts
- src/stores/executionStore.test.ts
- src/stores/firebaseAuthStore.test.ts
- src/stores/modelToNodeStore.test.ts
- src/stores/queueStore.test.ts
- src/stores/subgraphNavigationStore.test.ts
- src/stores/subgraphNavigationStore.viewport.test.ts
- src/stores/subgraphStore.test.ts
- src/stores/systemStatsStore.test.ts
- src/stores/workspace/nodeHelpStore.test.ts
- src/utils/colorUtil.test.ts
- src/utils/executableGroupNodeChildDTO.test.ts
Source files:
- src/stores/modelStore.ts - Improved type handling
### Testing
- All TypeScript type checking passes (`pnpm typecheck`)
- All affected test files pass (`pnpm test:unit`)
- Linting passes without errors (`pnpm lint`)
- Code formatting applied (`pnpm format`)
Part of the "Road to No Explicit Any" initiative, cleaning up type
casting issues from branch `fix/remove-any-types-part8`.
### Previous PRs in this series:
- Part 2: #7401
- Part 3: #7935
- Part 4: #7970
- Part 5: #8064
- Part 6: #8083
- Part 7: #8092
- Part 8 Group 1: #8253
- Part 8 Group 2: #8258
- Part 8 Group 3: #8304
- Part 8 Group 4: #8314
- Part 8 Group 5: #8329
- Part 8 Group 6: #8344
- Part 8 Group 7: #8459 (this PR)
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8459-Road-to-No-explicit-any-Group-8-part-7-test-files-2f86d73d36508114ad28d82e72a3a5e9)
by [Unito](https://www.unito.io)
This commit is contained in:
committed by
GitHub
parent
067d80c4ed
commit
13311a46ea
@@ -5,7 +5,7 @@ import { ComfyNodeDefImpl } from '@/stores/nodeDefStore'
|
||||
import { NodeSourceType } from '@/types/nodeSource'
|
||||
|
||||
describe('nodeOrganizationService', () => {
|
||||
const createMockNodeDef = (overrides: any = {}) => {
|
||||
const createMockNodeDef = (overrides: Partial<ComfyNodeDefImpl> = {}) => {
|
||||
const mockNodeDef = {
|
||||
name: 'TestNode',
|
||||
display_name: 'Test Node',
|
||||
@@ -273,7 +273,7 @@ describe('nodeOrganizationService', () => {
|
||||
it('should handle unknown source type', () => {
|
||||
const nodeDef = createMockNodeDef({
|
||||
nodeSource: {
|
||||
type: 'unknown' as any,
|
||||
type: 'unknown' as NodeSourceType,
|
||||
className: 'unknown',
|
||||
displayText: 'Unknown',
|
||||
badgeText: '?'
|
||||
|
||||
@@ -1,20 +1,34 @@
|
||||
import type { Mock } from 'vitest'
|
||||
import { liteClient as algoliasearch } from 'algoliasearch/dist/lite/builds/browser'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import type { components } from '@/types/comfyRegistryTypes'
|
||||
import { useAlgoliaSearchProvider } from '@/services/providers/algoliaSearchProvider'
|
||||
import { SortableAlgoliaField } from '@/workbench/extensions/manager/types/comfyManagerTypes'
|
||||
|
||||
type RegistryNodePack = components['schemas']['Node']
|
||||
|
||||
type GlobalWithAlgolia = typeof globalThis & {
|
||||
__ALGOLIA_APP_ID__: string
|
||||
__ALGOLIA_API_KEY__: string
|
||||
}
|
||||
|
||||
// Mock global Algolia constants
|
||||
;(global as any).__ALGOLIA_APP_ID__ = 'test-app-id'
|
||||
;(global as any).__ALGOLIA_API_KEY__ = 'test-api-key'
|
||||
const globalWithAlgolia = globalThis as GlobalWithAlgolia
|
||||
globalWithAlgolia.__ALGOLIA_APP_ID__ = 'test-app-id'
|
||||
globalWithAlgolia.__ALGOLIA_API_KEY__ = 'test-api-key'
|
||||
|
||||
// Mock algoliasearch
|
||||
vi.mock('algoliasearch/dist/lite/builds/browser', () => ({
|
||||
liteClient: vi.fn()
|
||||
}))
|
||||
|
||||
interface MockSearchClient {
|
||||
search: Mock
|
||||
}
|
||||
|
||||
describe('useAlgoliaSearchProvider', () => {
|
||||
let mockSearchClient: any
|
||||
let mockSearchClient: MockSearchClient
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
@@ -24,7 +38,11 @@ describe('useAlgoliaSearchProvider', () => {
|
||||
search: vi.fn()
|
||||
}
|
||||
|
||||
vi.mocked(algoliasearch).mockReturnValue(mockSearchClient)
|
||||
vi.mocked(algoliasearch).mockReturnValue(
|
||||
mockSearchClient as Partial<
|
||||
ReturnType<typeof algoliasearch>
|
||||
> as ReturnType<typeof algoliasearch>
|
||||
)
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
@@ -252,7 +270,7 @@ describe('useAlgoliaSearchProvider', () => {
|
||||
})
|
||||
|
||||
describe('getSortValue', () => {
|
||||
const testPack = {
|
||||
const testPack: Partial<RegistryNodePack> = {
|
||||
id: '1',
|
||||
name: 'Test Pack',
|
||||
downloads: 100,
|
||||
@@ -279,7 +297,10 @@ describe('useAlgoliaSearchProvider', () => {
|
||||
|
||||
const createdTimestamp = new Date('2024-01-01T10:00:00Z').getTime()
|
||||
expect(
|
||||
provider.getSortValue(testPack as any, SortableAlgoliaField.Created)
|
||||
provider.getSortValue(
|
||||
testPack as RegistryNodePack,
|
||||
SortableAlgoliaField.Created
|
||||
)
|
||||
).toBe(createdTimestamp)
|
||||
|
||||
const updatedTimestamp = new Date('2024-01-15T10:00:00Z').getTime()
|
||||
@@ -289,23 +310,35 @@ describe('useAlgoliaSearchProvider', () => {
|
||||
})
|
||||
|
||||
it('should handle missing values', () => {
|
||||
const incompletePack = { id: '1', name: 'Incomplete' }
|
||||
const incompletePack: Partial<RegistryNodePack> = {
|
||||
id: '1',
|
||||
name: 'Incomplete'
|
||||
}
|
||||
const provider = useAlgoliaSearchProvider()
|
||||
|
||||
expect(
|
||||
provider.getSortValue(incompletePack, SortableAlgoliaField.Downloads)
|
||||
provider.getSortValue(
|
||||
incompletePack as RegistryNodePack,
|
||||
SortableAlgoliaField.Downloads
|
||||
)
|
||||
).toBe(0)
|
||||
expect(
|
||||
provider.getSortValue(incompletePack, SortableAlgoliaField.Publisher)
|
||||
provider.getSortValue(
|
||||
incompletePack as RegistryNodePack,
|
||||
SortableAlgoliaField.Publisher
|
||||
)
|
||||
).toBe('')
|
||||
expect(
|
||||
provider.getSortValue(
|
||||
incompletePack as any,
|
||||
incompletePack as RegistryNodePack,
|
||||
SortableAlgoliaField.Created
|
||||
)
|
||||
).toBe(0)
|
||||
expect(
|
||||
provider.getSortValue(incompletePack, SortableAlgoliaField.Updated)
|
||||
provider.getSortValue(
|
||||
incompletePack as RegistryNodePack,
|
||||
SortableAlgoliaField.Updated
|
||||
)
|
||||
).toBe(0)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -14,20 +14,31 @@ describe('useComfyRegistrySearchProvider', () => {
|
||||
const mockListAllPacksCall = vi.fn()
|
||||
const mockListAllPacksClear = vi.fn()
|
||||
|
||||
const createMockStore = (
|
||||
params: Partial<ReturnType<typeof useComfyRegistryStore>> = {}
|
||||
) => {
|
||||
return {
|
||||
search: {
|
||||
call: mockSearchCall,
|
||||
clear: mockSearchClear,
|
||||
cancel: vi.fn()
|
||||
},
|
||||
listAllPacks: {
|
||||
call: mockListAllPacksCall,
|
||||
clear: mockListAllPacksClear,
|
||||
cancel: vi.fn()
|
||||
},
|
||||
...params
|
||||
} as Partial<ReturnType<typeof useComfyRegistryStore>> as ReturnType<
|
||||
typeof useComfyRegistryStore
|
||||
>
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
|
||||
// Setup store mock
|
||||
vi.mocked(useComfyRegistryStore).mockReturnValue({
|
||||
search: {
|
||||
call: mockSearchCall,
|
||||
clear: mockSearchClear
|
||||
},
|
||||
listAllPacks: {
|
||||
call: mockListAllPacksCall,
|
||||
clear: mockListAllPacksClear
|
||||
}
|
||||
} as any)
|
||||
vi.mocked(useComfyRegistryStore).mockReturnValue(createMockStore())
|
||||
})
|
||||
|
||||
describe('searchPacks', () => {
|
||||
|
||||
@@ -127,7 +127,9 @@ describe('useComfyRegistryStore', () => {
|
||||
}
|
||||
|
||||
vi.mocked(useComfyRegistryService).mockReturnValue(
|
||||
mockRegistryService as any
|
||||
mockRegistryService as Partial<
|
||||
ReturnType<typeof useComfyRegistryService>
|
||||
> as ReturnType<typeof useComfyRegistryService>
|
||||
)
|
||||
})
|
||||
|
||||
@@ -177,7 +179,7 @@ describe('useComfyRegistryStore', () => {
|
||||
const store = useComfyRegistryStore()
|
||||
vi.spyOn(store.getPackById, 'call').mockResolvedValueOnce(null)
|
||||
|
||||
const result = await store.getPackById.call(null as any)
|
||||
const result = await store.getPackById.call(null!)
|
||||
|
||||
expect(result).toBeNull()
|
||||
expect(mockRegistryService.getPackById).not.toHaveBeenCalled()
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { createTestingPinia } from '@pinia/testing'
|
||||
import { setActivePinia } from 'pinia'
|
||||
import { beforeEach, describe, expect, it } from 'vitest'
|
||||
|
||||
import type { LGraphNode } from '@/lib/litegraph/src/litegraph'
|
||||
import { useDomWidgetStore } from '@/stores/domWidgetStore'
|
||||
import { createTestingPinia } from '@pinia/testing'
|
||||
|
||||
// Mock DOM widget for testing
|
||||
const createMockDOMWidget = (id: string) => {
|
||||
@@ -15,7 +16,7 @@ const createMockDOMWidget = (id: string) => {
|
||||
title: 'Test Node',
|
||||
pos: [0, 0],
|
||||
size: [200, 100]
|
||||
} as any,
|
||||
} as Partial<LGraphNode> as LGraphNode,
|
||||
name: 'test_widget',
|
||||
type: 'text',
|
||||
value: 'test',
|
||||
@@ -23,7 +24,7 @@ const createMockDOMWidget = (id: string) => {
|
||||
y: 0,
|
||||
margin: 10,
|
||||
isVisible: () => true,
|
||||
containerNode: undefined as any
|
||||
containerNode: undefined
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import { createTestingPinia } from '@pinia/testing'
|
||||
import { setActivePinia } from 'pinia'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import { app } from '@/scripts/app'
|
||||
import { useExecutionStore } from '@/stores/executionStore'
|
||||
|
||||
@@ -11,6 +9,8 @@ const mockNodeIdToNodeLocatorId = vi.fn()
|
||||
const mockNodeLocatorIdToNodeExecutionId = vi.fn()
|
||||
|
||||
import type * as WorkflowStoreModule from '@/platform/workflow/management/stores/workflowStore'
|
||||
import { createMockLGraphNode } from '@/utils/__tests__/litegraphTestUtils'
|
||||
import { createTestingPinia } from '@pinia/testing'
|
||||
|
||||
// Mock the workflowStore
|
||||
vi.mock('@/platform/workflow/management/stores/workflowStore', async () => {
|
||||
@@ -72,12 +72,11 @@ describe('useExecutionStore - NodeLocatorId conversions', () => {
|
||||
nodes: []
|
||||
}
|
||||
|
||||
const mockNode = {
|
||||
const mockNode = createMockLGraphNode({
|
||||
id: 123,
|
||||
isSubgraphNode: () => true,
|
||||
subgraph: mockSubgraph
|
||||
} as any
|
||||
|
||||
})
|
||||
// Mock app.rootGraph.getNodeById to return the mock node
|
||||
vi.mocked(app.rootGraph.getNodeById).mockReturnValue(mockNode)
|
||||
|
||||
@@ -178,11 +177,11 @@ describe('useExecutionStore - Node Error Lookups', () => {
|
||||
nodes: []
|
||||
}
|
||||
|
||||
const mockNode = {
|
||||
const mockNode = createMockLGraphNode({
|
||||
id: 123,
|
||||
isSubgraphNode: () => true,
|
||||
subgraph: mockSubgraph
|
||||
} as any
|
||||
})
|
||||
|
||||
vi.mocked(app.rootGraph.getNodeById).mockReturnValue(mockNode)
|
||||
|
||||
|
||||
@@ -1,12 +1,28 @@
|
||||
import { FirebaseError } from 'firebase/app'
|
||||
import type { User, UserCredential } from 'firebase/auth'
|
||||
import * as firebaseAuth from 'firebase/auth'
|
||||
import { createTestingPinia } from '@pinia/testing'
|
||||
import { setActivePinia } from 'pinia'
|
||||
import type { Mock } from 'vitest'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import * as vuefire from 'vuefire'
|
||||
|
||||
import { useDialogService } from '@/services/dialogService'
|
||||
import { useFirebaseAuthStore } from '@/stores/firebaseAuthStore'
|
||||
import { createTestingPinia } from '@pinia/testing'
|
||||
|
||||
// Hoisted mocks for dynamic imports
|
||||
const { mockDistributionTypes } = vi.hoisted(() => ({
|
||||
mockDistributionTypes: {
|
||||
isCloud: true,
|
||||
isDesktop: true
|
||||
}
|
||||
}))
|
||||
|
||||
type MockUser = Omit<User, 'getIdToken'> & {
|
||||
getIdToken: Mock
|
||||
}
|
||||
|
||||
type MockAuth = Record<string, unknown>
|
||||
|
||||
// Mock fetch
|
||||
const mockFetch = vi.fn()
|
||||
@@ -83,35 +99,20 @@ vi.mock('@/stores/toastStore', () => ({
|
||||
// Mock useDialogService
|
||||
vi.mock('@/services/dialogService')
|
||||
|
||||
const mockDistributionTypes = vi.hoisted(() => ({
|
||||
isCloud: false,
|
||||
isDesktop: false
|
||||
}))
|
||||
|
||||
vi.mock('@/platform/distribution/types', () => mockDistributionTypes)
|
||||
|
||||
const mockApiKeyStore = vi.hoisted(() => ({
|
||||
getAuthHeader: vi.fn().mockReturnValue(null)
|
||||
}))
|
||||
|
||||
vi.mock('@/stores/apiKeyAuthStore', () => ({
|
||||
useApiKeyAuthStore: () => mockApiKeyStore
|
||||
}))
|
||||
|
||||
describe('useFirebaseAuthStore', () => {
|
||||
let store: ReturnType<typeof useFirebaseAuthStore>
|
||||
let authStateCallback: (user: any) => void
|
||||
let idTokenCallback: (user: any) => void
|
||||
let authStateCallback: (user: User | null) => void
|
||||
let idTokenCallback: (user: User | null) => void
|
||||
|
||||
const mockAuth = {
|
||||
const mockAuth: MockAuth = {
|
||||
/* mock Auth object */
|
||||
}
|
||||
|
||||
const mockUser = {
|
||||
const mockUser: MockUser = {
|
||||
uid: 'test-user-id',
|
||||
email: 'test@example.com',
|
||||
getIdToken: vi.fn().mockResolvedValue('mock-id-token')
|
||||
}
|
||||
} as Partial<User> as MockUser
|
||||
|
||||
beforeEach(() => {
|
||||
vi.resetAllMocks()
|
||||
@@ -123,14 +124,18 @@ describe('useFirebaseAuthStore', () => {
|
||||
})
|
||||
|
||||
// Mock useFirebaseAuth to return our mock auth object
|
||||
vi.mocked(vuefire.useFirebaseAuth).mockReturnValue(mockAuth as any)
|
||||
vi.mocked(vuefire.useFirebaseAuth).mockReturnValue(
|
||||
mockAuth as Partial<
|
||||
ReturnType<typeof vuefire.useFirebaseAuth>
|
||||
> as ReturnType<typeof vuefire.useFirebaseAuth>
|
||||
)
|
||||
|
||||
// Mock onAuthStateChanged to capture the callback and simulate initial auth state
|
||||
vi.mocked(firebaseAuth.onAuthStateChanged).mockImplementation(
|
||||
(_, callback) => {
|
||||
authStateCallback = callback as (user: any) => void
|
||||
authStateCallback = callback as (user: User | null) => void
|
||||
// Call the callback with our mock user
|
||||
;(callback as (user: any) => void)(mockUser)
|
||||
;(callback as (user: User | null) => void)(mockUser)
|
||||
// Return an unsubscribe function
|
||||
return vi.fn()
|
||||
}
|
||||
@@ -163,21 +168,26 @@ describe('useFirebaseAuthStore', () => {
|
||||
})
|
||||
|
||||
describe('token refresh events', () => {
|
||||
beforeEach(() => {
|
||||
mockDistributionTypes.isCloud = true
|
||||
mockDistributionTypes.isDesktop = true
|
||||
beforeEach(async () => {
|
||||
vi.resetModules()
|
||||
vi.mock('@/platform/distribution/types', () => mockDistributionTypes)
|
||||
|
||||
vi.mocked(firebaseAuth.onIdTokenChanged).mockImplementation(
|
||||
(_auth, callback) => {
|
||||
idTokenCallback = callback as (user: any) => void
|
||||
idTokenCallback = callback as (user: User | null) => void
|
||||
return vi.fn()
|
||||
}
|
||||
)
|
||||
|
||||
vi.mocked(vuefire.useFirebaseAuth).mockReturnValue(mockAuth as any)
|
||||
vi.mocked(vuefire.useFirebaseAuth).mockReturnValue(
|
||||
mockAuth as Partial<
|
||||
ReturnType<typeof vuefire.useFirebaseAuth>
|
||||
> as ReturnType<typeof vuefire.useFirebaseAuth>
|
||||
)
|
||||
|
||||
setActivePinia(createTestingPinia({ stubActions: false }))
|
||||
store = useFirebaseAuthStore()
|
||||
const storeModule = await import('@/stores/firebaseAuthStore')
|
||||
store = storeModule.useFirebaseAuthStore()
|
||||
})
|
||||
|
||||
it("should not increment tokenRefreshTrigger on the user's first ID token event", () => {
|
||||
@@ -192,14 +202,14 @@ describe('useFirebaseAuthStore', () => {
|
||||
})
|
||||
|
||||
it('should not increment when ID token event is for a different user UID', () => {
|
||||
const otherUser = { uid: 'other-user-id' }
|
||||
const otherUser = { uid: 'other-user-id' } as Partial<User> as User
|
||||
idTokenCallback?.(mockUser)
|
||||
idTokenCallback?.(otherUser)
|
||||
expect(store.tokenRefreshTrigger).toBe(0)
|
||||
})
|
||||
|
||||
it('should increment after switching to a new UID and receiving a second event for that UID', () => {
|
||||
const otherUser = { uid: 'other-user-id' }
|
||||
const otherUser = { uid: 'other-user-id' } as Partial<User> as User
|
||||
idTokenCallback?.(mockUser)
|
||||
idTokenCallback?.(otherUser)
|
||||
idTokenCallback?.(otherUser)
|
||||
@@ -238,7 +248,7 @@ describe('useFirebaseAuthStore', () => {
|
||||
// Now, succeed on next attempt
|
||||
vi.mocked(firebaseAuth.signInWithEmailAndPassword).mockResolvedValueOnce({
|
||||
user: mockUser
|
||||
} as any)
|
||||
} as Partial<UserCredential> as UserCredential)
|
||||
|
||||
await store.login('test@example.com', 'correct-password')
|
||||
})
|
||||
@@ -247,7 +257,7 @@ describe('useFirebaseAuthStore', () => {
|
||||
it('should login with valid credentials', async () => {
|
||||
const mockUserCredential = { user: mockUser }
|
||||
vi.mocked(firebaseAuth.signInWithEmailAndPassword).mockResolvedValue(
|
||||
mockUserCredential as any
|
||||
mockUserCredential as Partial<UserCredential> as UserCredential
|
||||
)
|
||||
|
||||
const result = await store.login('test@example.com', 'password')
|
||||
@@ -283,7 +293,7 @@ describe('useFirebaseAuthStore', () => {
|
||||
// Set up multiple login promises
|
||||
const mockUserCredential = { user: mockUser }
|
||||
vi.mocked(firebaseAuth.signInWithEmailAndPassword).mockResolvedValue(
|
||||
mockUserCredential as any
|
||||
mockUserCredential as Partial<UserCredential> as UserCredential
|
||||
)
|
||||
|
||||
const loginPromise1 = store.login('user1@example.com', 'password1')
|
||||
@@ -301,7 +311,7 @@ describe('useFirebaseAuthStore', () => {
|
||||
it('should register a new user', async () => {
|
||||
const mockUserCredential = { user: mockUser }
|
||||
vi.mocked(firebaseAuth.createUserWithEmailAndPassword).mockResolvedValue(
|
||||
mockUserCredential as any
|
||||
mockUserCredential as Partial<UserCredential> as UserCredential
|
||||
)
|
||||
|
||||
const result = await store.register('new@example.com', 'password')
|
||||
@@ -378,7 +388,7 @@ describe('useFirebaseAuthStore', () => {
|
||||
// Setup mock for login
|
||||
const mockUserCredential = { user: mockUser }
|
||||
vi.mocked(firebaseAuth.signInWithEmailAndPassword).mockResolvedValue(
|
||||
mockUserCredential as any
|
||||
mockUserCredential as Partial<UserCredential> as UserCredential
|
||||
)
|
||||
|
||||
// Login
|
||||
@@ -454,9 +464,6 @@ describe('useFirebaseAuthStore', () => {
|
||||
// This test reproduces the issue where getAuthHeader fails due to network errors
|
||||
// when Firebase Auth tries to refresh tokens offline
|
||||
|
||||
// Configure mockApiKeyStore to return null (no API key fallback)
|
||||
mockApiKeyStore.getAuthHeader.mockReturnValue(null)
|
||||
|
||||
// Setup user with network error on token refresh
|
||||
mockUser.getIdToken.mockReset()
|
||||
const networkError = new FirebaseError(
|
||||
@@ -475,7 +482,7 @@ describe('useFirebaseAuthStore', () => {
|
||||
it('should sign in with Google', async () => {
|
||||
const mockUserCredential = { user: mockUser }
|
||||
vi.mocked(firebaseAuth.signInWithPopup).mockResolvedValue(
|
||||
mockUserCredential as any
|
||||
mockUserCredential as Partial<UserCredential> as UserCredential
|
||||
)
|
||||
|
||||
const result = await store.loginWithGoogle()
|
||||
@@ -508,7 +515,7 @@ describe('useFirebaseAuthStore', () => {
|
||||
it('should sign in with Github', async () => {
|
||||
const mockUserCredential = { user: mockUser }
|
||||
vi.mocked(firebaseAuth.signInWithPopup).mockResolvedValue(
|
||||
mockUserCredential as any
|
||||
mockUserCredential as Partial<UserCredential> as UserCredential
|
||||
)
|
||||
|
||||
const result = await store.loginWithGithub()
|
||||
@@ -540,7 +547,7 @@ describe('useFirebaseAuthStore', () => {
|
||||
it('should handle concurrent social login attempts correctly', async () => {
|
||||
const mockUserCredential = { user: mockUser }
|
||||
vi.mocked(firebaseAuth.signInWithPopup).mockResolvedValue(
|
||||
mockUserCredential as any
|
||||
mockUserCredential as Partial<UserCredential> as UserCredential
|
||||
)
|
||||
|
||||
const googleLoginPromise = store.loginWithGoogle()
|
||||
|
||||
@@ -7,14 +7,19 @@ import { useSettingStore } from '@/platform/settings/settingStore'
|
||||
import { api } from '@/scripts/api'
|
||||
|
||||
/** (Internal helper) finds a value in a metadata object from any of a list of keys. */
|
||||
function _findInMetadata(metadata: any, ...keys: string[]): string | null {
|
||||
function _findInMetadata(
|
||||
metadata: Record<string, string | null>,
|
||||
...keys: string[]
|
||||
): string | null {
|
||||
for (const key of keys) {
|
||||
if (key in metadata) {
|
||||
return metadata[key]
|
||||
const value = metadata[key]
|
||||
return value || null
|
||||
}
|
||||
for (const k in metadata) {
|
||||
if (k.endsWith(key)) {
|
||||
return metadata[k]
|
||||
const value = metadata[k]
|
||||
return value || null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,7 +239,7 @@ describe('useModelToNodeStore', () => {
|
||||
it('should not register provider when nodeDef is undefined', () => {
|
||||
const modelToNodeStore = useModelToNodeStore()
|
||||
const providerWithoutNodeDef = new ModelNodeProvider(
|
||||
undefined as any,
|
||||
undefined!,
|
||||
'custom_key'
|
||||
)
|
||||
|
||||
@@ -507,15 +507,11 @@ describe('useModelToNodeStore', () => {
|
||||
modelToNodeStore.registerDefaults()
|
||||
|
||||
// These should not throw but return undefined
|
||||
expect(modelToNodeStore.getCategoryForNodeType(null!)).toBeUndefined()
|
||||
expect(
|
||||
modelToNodeStore.getCategoryForNodeType(null as any)
|
||||
).toBeUndefined()
|
||||
expect(
|
||||
modelToNodeStore.getCategoryForNodeType(undefined as any)
|
||||
).toBeUndefined()
|
||||
expect(
|
||||
modelToNodeStore.getCategoryForNodeType(123 as any)
|
||||
modelToNodeStore.getCategoryForNodeType(undefined!)
|
||||
).toBeUndefined()
|
||||
expect(modelToNodeStore.getCategoryForNodeType('123')).toBeUndefined()
|
||||
})
|
||||
|
||||
it('should be case-sensitive for node type matching', () => {
|
||||
|
||||
@@ -38,7 +38,11 @@ function createHistoryJob(createTime: number, id: string): JobListItem {
|
||||
|
||||
const createTaskOutput = (
|
||||
nodeId: string = 'node-1',
|
||||
images: any[] = []
|
||||
images: {
|
||||
type?: 'output' | 'input' | 'temp'
|
||||
filename?: string
|
||||
subfolder?: string
|
||||
}[] = []
|
||||
): TaskOutput => ({
|
||||
[nodeId]: {
|
||||
images
|
||||
@@ -490,7 +494,7 @@ describe('useQueueStore', () => {
|
||||
it('should recreate TaskItemImpl when outputs_count changes', async () => {
|
||||
// Initial load without outputs_count
|
||||
const jobWithoutOutputsCount = createHistoryJob(10, 'job-1')
|
||||
delete (jobWithoutOutputsCount as any).outputs_count
|
||||
delete jobWithoutOutputsCount.outputs_count
|
||||
|
||||
mockGetQueue.mockResolvedValue({ Running: [], Pending: [] })
|
||||
mockGetHistory.mockResolvedValue([jobWithoutOutputsCount])
|
||||
|
||||
@@ -7,7 +7,9 @@ import { useWorkflowStore } from '@/platform/workflow/management/stores/workflow
|
||||
import type { ComfyWorkflow } from '@/platform/workflow/management/stores/workflowStore'
|
||||
import { app } from '@/scripts/app'
|
||||
import { useSubgraphNavigationStore } from '@/stores/subgraphNavigationStore'
|
||||
import { findSubgraphPathById } from '@/utils/graphTraversalUtil'
|
||||
import { createMockChangeTracker } from '@/utils/__tests__/litegraphTestUtils'
|
||||
|
||||
import type { Subgraph } from '@/lib/litegraph/src/LGraph'
|
||||
|
||||
vi.mock('@/scripts/app', () => {
|
||||
const mockCanvas = {
|
||||
@@ -38,7 +40,7 @@ vi.mock('@/scripts/app', () => {
|
||||
|
||||
vi.mock('@/renderer/core/canvas/canvasStore', () => ({
|
||||
useCanvasStore: () => ({
|
||||
getCanvas: () => (app as any).canvas
|
||||
getCanvas: () => app.canvas
|
||||
})
|
||||
}))
|
||||
|
||||
@@ -63,7 +65,8 @@ describe('useSubgraphNavigationStore', () => {
|
||||
} as ComfyWorkflow
|
||||
|
||||
// Set the active workflow (cast to bypass TypeScript check in test)
|
||||
workflowStore.activeWorkflow = mockWorkflow as any
|
||||
workflowStore.activeWorkflow =
|
||||
mockWorkflow as typeof workflowStore.activeWorkflow
|
||||
|
||||
// Simulate being in a subgraph by restoring state
|
||||
navigationStore.restoreState(['subgraph-1', 'subgraph-2'])
|
||||
@@ -72,7 +75,9 @@ describe('useSubgraphNavigationStore', () => {
|
||||
|
||||
// Simulate a change to the workflow's internal state
|
||||
// (e.g., changeTracker.activeState being reassigned)
|
||||
mockWorkflow.changeTracker = { activeState: {} } as any
|
||||
mockWorkflow.changeTracker = {
|
||||
activeState: {}
|
||||
} as typeof mockWorkflow.changeTracker
|
||||
|
||||
// The navigation stack should NOT be cleared because the path hasn't changed
|
||||
expect(navigationStore.exportState()).toHaveLength(2)
|
||||
@@ -87,14 +92,15 @@ describe('useSubgraphNavigationStore', () => {
|
||||
const workflow1 = {
|
||||
path: 'workflow1.json',
|
||||
filename: 'workflow1.json',
|
||||
changeTracker: {
|
||||
changeTracker: createMockChangeTracker({
|
||||
restore: vi.fn(),
|
||||
store: vi.fn()
|
||||
}
|
||||
} as unknown as ComfyWorkflow
|
||||
})
|
||||
} as Partial<ComfyWorkflow> as ComfyWorkflow
|
||||
|
||||
// Set the active workflow
|
||||
workflowStore.activeWorkflow = workflow1 as any
|
||||
workflowStore.activeWorkflow =
|
||||
workflow1 as typeof workflowStore.activeWorkflow
|
||||
|
||||
// Simulate the restore process that happens when loading a workflow
|
||||
// Since subgraphState is private, we'll simulate the effect by directly restoring navigation
|
||||
@@ -108,13 +114,14 @@ describe('useSubgraphNavigationStore', () => {
|
||||
const workflow2 = {
|
||||
path: 'workflow2.json',
|
||||
filename: 'workflow2.json',
|
||||
changeTracker: {
|
||||
changeTracker: createMockChangeTracker({
|
||||
restore: vi.fn(),
|
||||
store: vi.fn()
|
||||
}
|
||||
} as unknown as ComfyWorkflow
|
||||
})
|
||||
} as Partial<ComfyWorkflow> as ComfyWorkflow
|
||||
|
||||
workflowStore.activeWorkflow = workflow2 as any
|
||||
workflowStore.activeWorkflow =
|
||||
workflow2 as typeof workflowStore.activeWorkflow
|
||||
|
||||
// Simulate the restore process for workflow2
|
||||
// Since subgraphState is private, we'll simulate the effect by directly restoring navigation
|
||||
@@ -124,7 +131,8 @@ describe('useSubgraphNavigationStore', () => {
|
||||
expect(navigationStore.exportState()).toHaveLength(0)
|
||||
|
||||
// Switch back to workflow1
|
||||
workflowStore.activeWorkflow = workflow1 as any
|
||||
workflowStore.activeWorkflow =
|
||||
workflow1 as typeof workflowStore.activeWorkflow
|
||||
|
||||
// Simulate the restore process for workflow1 again
|
||||
// Since subgraphState is private, we'll simulate the effect by directly restoring navigation
|
||||
@@ -138,17 +146,18 @@ describe('useSubgraphNavigationStore', () => {
|
||||
it('should clear navigation when activeSubgraph becomes undefined', async () => {
|
||||
const navigationStore = useSubgraphNavigationStore()
|
||||
const workflowStore = useWorkflowStore()
|
||||
const { findSubgraphPathById } = await import('@/utils/graphTraversalUtil')
|
||||
|
||||
// Create mock subgraph and graph structure
|
||||
const mockSubgraph = {
|
||||
id: 'subgraph-1',
|
||||
rootGraph: (app as any).graph,
|
||||
rootGraph: app.graph,
|
||||
_nodes: [],
|
||||
nodes: []
|
||||
}
|
||||
} as Partial<Subgraph> as Subgraph
|
||||
|
||||
// Add the subgraph to the graph's subgraphs map
|
||||
;(app as any).graph.subgraphs.set('subgraph-1', mockSubgraph)
|
||||
app.graph.subgraphs.set('subgraph-1', mockSubgraph)
|
||||
|
||||
// First set an active workflow
|
||||
const mockWorkflow = {
|
||||
@@ -156,13 +165,14 @@ describe('useSubgraphNavigationStore', () => {
|
||||
filename: 'test-workflow.json'
|
||||
} as ComfyWorkflow
|
||||
|
||||
workflowStore.activeWorkflow = mockWorkflow as any
|
||||
workflowStore.activeWorkflow =
|
||||
mockWorkflow as typeof workflowStore.activeWorkflow
|
||||
|
||||
// Mock findSubgraphPathById to return the correct path
|
||||
vi.mocked(findSubgraphPathById).mockReturnValue(['subgraph-1'])
|
||||
|
||||
// Set canvas.subgraph and trigger update to set activeSubgraph
|
||||
;(app as any).canvas.subgraph = mockSubgraph
|
||||
app.canvas.subgraph = mockSubgraph
|
||||
workflowStore.updateActiveGraph()
|
||||
|
||||
// Wait for Vue's reactivity to process the change
|
||||
@@ -173,7 +183,7 @@ describe('useSubgraphNavigationStore', () => {
|
||||
expect(navigationStore.exportState()).toEqual(['subgraph-1'])
|
||||
|
||||
// Clear canvas.subgraph and trigger update (simulating navigating back to root)
|
||||
;(app as any).canvas.subgraph = null
|
||||
app.canvas.subgraph = undefined
|
||||
workflowStore.updateActiveGraph()
|
||||
|
||||
// Wait for Vue's reactivity to process the change
|
||||
|
||||
@@ -3,11 +3,16 @@ import { setActivePinia } from 'pinia'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { nextTick } from 'vue'
|
||||
|
||||
import type { LGraph, Subgraph } from '@/lib/litegraph/src/litegraph'
|
||||
import { useWorkflowStore } from '@/platform/workflow/management/stores/workflowStore'
|
||||
import type { ComfyWorkflow } from '@/platform/workflow/management/stores/workflowStore'
|
||||
import { app } from '@/scripts/app'
|
||||
import { useSubgraphNavigationStore } from '@/stores/subgraphNavigationStore'
|
||||
|
||||
const { mockSetDirty } = vi.hoisted(() => ({
|
||||
mockSetDirty: vi.fn()
|
||||
}))
|
||||
|
||||
vi.mock('@/scripts/app', () => {
|
||||
const mockCanvas = {
|
||||
subgraph: null,
|
||||
@@ -19,7 +24,7 @@ vi.mock('@/scripts/app', () => {
|
||||
offset: [0, 0]
|
||||
}
|
||||
},
|
||||
setDirty: vi.fn()
|
||||
setDirty: mockSetDirty
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -38,12 +43,12 @@ vi.mock('@/scripts/app', () => {
|
||||
// Mock canvasStore
|
||||
vi.mock('@/renderer/core/canvas/canvasStore', () => ({
|
||||
useCanvasStore: () => ({
|
||||
getCanvas: () => (app as any).canvas
|
||||
getCanvas: () => app.canvas
|
||||
})
|
||||
}))
|
||||
|
||||
// Get reference to mock canvas
|
||||
const mockCanvas = app.canvas as any
|
||||
const mockCanvas = app.canvas
|
||||
|
||||
describe('useSubgraphNavigationStore - Viewport Persistence', () => {
|
||||
beforeEach(() => {
|
||||
@@ -53,7 +58,7 @@ describe('useSubgraphNavigationStore - Viewport Persistence', () => {
|
||||
mockCanvas.ds.offset = [0, 0]
|
||||
mockCanvas.ds.state.scale = 1
|
||||
mockCanvas.ds.state.offset = [0, 0]
|
||||
mockCanvas.setDirty.mockClear()
|
||||
mockSetDirty.mockClear()
|
||||
})
|
||||
|
||||
describe('saveViewport', () => {
|
||||
@@ -99,7 +104,7 @@ describe('useSubgraphNavigationStore - Viewport Persistence', () => {
|
||||
|
||||
// Mock being in a subgraph
|
||||
const mockSubgraph = { id: 'sub-456' }
|
||||
workflowStore.activeSubgraph = mockSubgraph as any
|
||||
workflowStore.activeSubgraph = mockSubgraph as Subgraph
|
||||
|
||||
// Set viewport state
|
||||
mockCanvas.ds.state.scale = 3
|
||||
@@ -159,7 +164,7 @@ describe('useSubgraphNavigationStore - Viewport Persistence', () => {
|
||||
// Reset canvas
|
||||
mockCanvas.ds.scale = 1
|
||||
mockCanvas.ds.offset = [0, 0]
|
||||
mockCanvas.setDirty.mockClear()
|
||||
mockSetDirty.mockClear()
|
||||
|
||||
// Try to restore non-existent viewport
|
||||
navigationStore.restoreViewport('non-existent')
|
||||
@@ -167,7 +172,7 @@ describe('useSubgraphNavigationStore - Viewport Persistence', () => {
|
||||
// Canvas should not change
|
||||
expect(mockCanvas.ds.scale).toBe(1)
|
||||
expect(mockCanvas.ds.offset).toEqual([0, 0])
|
||||
expect(mockCanvas.setDirty).not.toHaveBeenCalled()
|
||||
expect(mockSetDirty).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -182,7 +187,7 @@ describe('useSubgraphNavigationStore - Viewport Persistence', () => {
|
||||
nodes: [],
|
||||
subgraphs: new Map(),
|
||||
getNodeById: vi.fn()
|
||||
}
|
||||
} as Partial<LGraph> as LGraph
|
||||
const subgraph1 = {
|
||||
id: 'sub1',
|
||||
rootGraph: mockRootGraph,
|
||||
@@ -195,7 +200,7 @@ describe('useSubgraphNavigationStore - Viewport Persistence', () => {
|
||||
mockCanvas.ds.state.offset = [100, 100]
|
||||
|
||||
// Navigate to subgraph
|
||||
workflowStore.activeSubgraph = subgraph1 as any
|
||||
workflowStore.activeSubgraph = subgraph1 as Partial<Subgraph> as Subgraph
|
||||
await nextTick()
|
||||
|
||||
// Root viewport should have been saved automatically
|
||||
@@ -240,10 +245,14 @@ describe('useSubgraphNavigationStore - Viewport Persistence', () => {
|
||||
const workflow1 = { path: 'workflow1.json' } as ComfyWorkflow
|
||||
const workflow2 = { path: 'workflow2.json' } as ComfyWorkflow
|
||||
|
||||
workflowStore.activeWorkflow = workflow1 as any
|
||||
workflowStore.activeWorkflow = workflow1 as ReturnType<
|
||||
typeof useWorkflowStore
|
||||
>['activeWorkflow']
|
||||
await nextTick()
|
||||
|
||||
workflowStore.activeWorkflow = workflow2 as any
|
||||
workflowStore.activeWorkflow = workflow2 as ReturnType<
|
||||
typeof useWorkflowStore
|
||||
>['activeWorkflow']
|
||||
await nextTick()
|
||||
|
||||
// Cache should be preserved (LRU will manage memory)
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { createTestingPinia } from '@pinia/testing'
|
||||
import { setActivePinia } from 'pinia'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import type { ComfyNodeDef as ComfyNodeDefV1 } from '@/schemas/nodeDefSchema'
|
||||
import type { GlobalSubgraphData } from '@/scripts/api'
|
||||
import type { ExportedSubgraph } from '@/lib/litegraph/src/types/serialisation'
|
||||
import { api } from '@/scripts/api'
|
||||
import { app as comfyApp } from '@/scripts/app'
|
||||
import { useLitegraphService } from '@/services/litegraphService'
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
createTestSubgraph,
|
||||
createTestSubgraphNode
|
||||
} from '@/lib/litegraph/src/subgraph/__fixtures__/subgraphHelpers'
|
||||
import { createTestingPinia } from '@pinia/testing'
|
||||
|
||||
// Mock telemetry to break circular dependency (telemetry → workflowStore → app → telemetry)
|
||||
vi.mock('@/platform/telemetry', () => ({
|
||||
@@ -96,10 +97,18 @@ describe('useSubgraphStore', () => {
|
||||
const graph = subgraphNode.graph
|
||||
graph.add(subgraphNode)
|
||||
vi.mocked(comfyApp.canvas).selectedItems = new Set([subgraphNode])
|
||||
vi.mocked(comfyApp.canvas)._serializeItems = vi.fn(() => ({
|
||||
nodes: [subgraphNode.serialize()],
|
||||
subgraphs: [subgraph.serialize() as any]
|
||||
}))
|
||||
vi.mocked(comfyApp.canvas)._serializeItems = vi.fn(() => {
|
||||
const serializedSubgraph = {
|
||||
...subgraph.serialize(),
|
||||
links: [],
|
||||
groups: [],
|
||||
version: 1
|
||||
} as Partial<ExportedSubgraph> as ExportedSubgraph
|
||||
return {
|
||||
nodes: [subgraphNode.serialize()],
|
||||
subgraphs: [serializedSubgraph]
|
||||
}
|
||||
})
|
||||
//mock saving of file
|
||||
vi.mocked(api.storeUserData).mockResolvedValue({
|
||||
status: 200,
|
||||
|
||||
@@ -2,6 +2,7 @@ import { createTestingPinia } from '@pinia/testing'
|
||||
import { setActivePinia } from 'pinia'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import type { SystemStats } from '@/schemas/apiSchema'
|
||||
import { api } from '@/scripts/api'
|
||||
import { useSystemStatsStore } from '@/stores/systemStatsStore'
|
||||
import { isElectron } from '@/utils/envUtil'
|
||||
@@ -25,7 +26,7 @@ describe('useSystemStatsStore', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
// Mock API to prevent automatic fetch on store creation
|
||||
vi.mocked(api.getSystemStats).mockResolvedValue(null as any)
|
||||
vi.mocked(api.getSystemStats).mockResolvedValue(null!)
|
||||
setActivePinia(createTestingPinia({ stubActions: false }))
|
||||
store = useSystemStatsStore()
|
||||
vi.clearAllMocks()
|
||||
@@ -90,8 +91,8 @@ describe('useSystemStatsStore', () => {
|
||||
})
|
||||
|
||||
it('should set loading state correctly', async () => {
|
||||
let resolvePromise: (value: any) => void = () => {}
|
||||
const promise = new Promise<any>((resolve) => {
|
||||
let resolvePromise: (value: SystemStats) => void = () => {}
|
||||
const promise = new Promise<SystemStats>((resolve) => {
|
||||
resolvePromise = resolve
|
||||
})
|
||||
vi.mocked(api.getSystemStats).mockReturnValue(promise)
|
||||
@@ -99,7 +100,7 @@ describe('useSystemStatsStore', () => {
|
||||
const fetchPromise = store.refetchSystemStats()
|
||||
expect(store.isLoading).toBe(true)
|
||||
|
||||
resolvePromise({})
|
||||
resolvePromise({} as SystemStats)
|
||||
await fetchPromise
|
||||
|
||||
expect(store.isLoading).toBe(false)
|
||||
@@ -153,7 +154,7 @@ describe('useSystemStatsStore', () => {
|
||||
argv: [],
|
||||
ram_total: 16000000000,
|
||||
ram_free: 8000000000
|
||||
} as any,
|
||||
} as Partial<SystemStats['system']> as SystemStats['system'],
|
||||
devices: []
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import { createTestingPinia } from '@pinia/testing'
|
||||
import { setActivePinia } from 'pinia'
|
||||
import { beforeEach, describe, expect, it } from 'vitest'
|
||||
|
||||
import type { ComfyNodeDefImpl } from '@/stores/nodeDefStore'
|
||||
import { useNodeHelpStore } from '@/stores/workspace/nodeHelpStore'
|
||||
|
||||
describe('nodeHelpStore', () => {
|
||||
@@ -27,7 +28,9 @@ describe('nodeHelpStore', () => {
|
||||
it('should open help for a node', () => {
|
||||
const nodeHelpStore = useNodeHelpStore()
|
||||
|
||||
nodeHelpStore.openHelp(mockCoreNode as any)
|
||||
nodeHelpStore.openHelp(
|
||||
mockCoreNode as Partial<ComfyNodeDefImpl> as ComfyNodeDefImpl
|
||||
)
|
||||
|
||||
expect(nodeHelpStore.currentHelpNode).toStrictEqual(mockCoreNode)
|
||||
expect(nodeHelpStore.isHelpOpen).toBe(true)
|
||||
@@ -36,7 +39,9 @@ describe('nodeHelpStore', () => {
|
||||
it('should close help', () => {
|
||||
const nodeHelpStore = useNodeHelpStore()
|
||||
|
||||
nodeHelpStore.openHelp(mockCoreNode as any)
|
||||
nodeHelpStore.openHelp(
|
||||
mockCoreNode as Partial<ComfyNodeDefImpl> as ComfyNodeDefImpl
|
||||
)
|
||||
expect(nodeHelpStore.isHelpOpen).toBe(true)
|
||||
|
||||
nodeHelpStore.closeHelp()
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import type { ColorAdjustOptions } from '@/utils/colorUtil'
|
||||
import {
|
||||
adjustColor,
|
||||
hexToRgb,
|
||||
@@ -22,13 +23,16 @@ interface ColorTestCase {
|
||||
type ColorFormat = 'hex' | 'rgb' | 'rgba' | 'hsl' | 'hsla'
|
||||
|
||||
vi.mock('es-toolkit/compat', () => ({
|
||||
memoize: (fn: any) => fn
|
||||
memoize: <T extends (...args: unknown[]) => unknown>(fn: T) => fn
|
||||
}))
|
||||
|
||||
const targetOpacity = 0.5
|
||||
const targetLightness = 0.5
|
||||
|
||||
const assertColorVariationsMatch = (variations: string[], adjustment: any) => {
|
||||
const assertColorVariationsMatch = (
|
||||
variations: string[],
|
||||
adjustment: ColorAdjustOptions
|
||||
) => {
|
||||
for (let i = 0; i < variations.length - 1; i++) {
|
||||
expect(adjustColor(variations[i], adjustment)).toBe(
|
||||
adjustColor(variations[i + 1], adjustment)
|
||||
|
||||
@@ -7,6 +7,7 @@ import type {
|
||||
LGraphNode
|
||||
} from '@/lib/litegraph/src/litegraph'
|
||||
import { ExecutableGroupNodeChildDTO } from '@/utils/executableGroupNodeChildDTO'
|
||||
import { createMockLGraphNode } from './__tests__/litegraphTestUtils'
|
||||
|
||||
describe('ExecutableGroupNodeChildDTO', () => {
|
||||
let mockNode: LGraphNode
|
||||
@@ -16,18 +17,18 @@ describe('ExecutableGroupNodeChildDTO', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
// Create mock nodes
|
||||
mockNode = {
|
||||
mockNode = createMockLGraphNode({
|
||||
id: '3', // Simple node ID for most tests
|
||||
graph: {},
|
||||
getInputNode: vi.fn(),
|
||||
getInputLink: vi.fn(),
|
||||
inputs: []
|
||||
} as any
|
||||
})
|
||||
|
||||
mockInputNode = {
|
||||
mockInputNode = createMockLGraphNode({
|
||||
id: '1',
|
||||
graph: {}
|
||||
} as any
|
||||
})
|
||||
|
||||
// Create the nodesByExecutionId map
|
||||
mockNodesByExecutionId = new Map()
|
||||
@@ -38,7 +39,7 @@ describe('ExecutableGroupNodeChildDTO', () => {
|
||||
describe('resolveInput', () => {
|
||||
it('should resolve input from external node (node outside the group)', () => {
|
||||
// Setup: Group node child with ID '10:3'
|
||||
const groupNodeChild = {
|
||||
const groupNodeChild = createMockLGraphNode({
|
||||
id: '10:3',
|
||||
graph: {},
|
||||
getInputNode: vi.fn().mockReturnValue(mockInputNode),
|
||||
@@ -46,7 +47,7 @@ describe('ExecutableGroupNodeChildDTO', () => {
|
||||
origin_slot: 0
|
||||
}),
|
||||
inputs: []
|
||||
} as any
|
||||
})
|
||||
|
||||
// External node with ID '1'
|
||||
const externalNodeDto = {
|
||||
@@ -75,19 +76,19 @@ describe('ExecutableGroupNodeChildDTO', () => {
|
||||
|
||||
it('should resolve input from internal node (node inside the same group)', () => {
|
||||
// Setup: Group node child with ID '10:3'
|
||||
const groupNodeChild = {
|
||||
const groupNodeChild = createMockLGraphNode({
|
||||
id: '10:3',
|
||||
graph: {},
|
||||
getInputNode: vi.fn(),
|
||||
getInputLink: vi.fn(),
|
||||
inputs: []
|
||||
} as any
|
||||
})
|
||||
|
||||
// Internal node with ID '10:2'
|
||||
const internalInputNode = {
|
||||
const internalInputNode = createMockLGraphNode({
|
||||
id: '10:2',
|
||||
graph: {}
|
||||
} as LGraphNode
|
||||
})
|
||||
|
||||
const internalNodeDto = {
|
||||
id: '2',
|
||||
@@ -97,10 +98,10 @@ describe('ExecutableGroupNodeChildDTO', () => {
|
||||
// Internal nodes are stored with just their index
|
||||
mockNodesByExecutionId.set('2', internalNodeDto)
|
||||
|
||||
groupNodeChild.getInputNode.mockReturnValue(internalInputNode)
|
||||
groupNodeChild.getInputLink.mockReturnValue({
|
||||
vi.mocked(groupNodeChild.getInputNode).mockReturnValue(internalInputNode)
|
||||
vi.mocked(groupNodeChild.getInputLink).mockReturnValue({
|
||||
origin_slot: 1
|
||||
})
|
||||
} as ReturnType<LGraphNode['getInputLink']>)
|
||||
|
||||
const dto = new ExecutableGroupNodeChildDTO(
|
||||
groupNodeChild,
|
||||
@@ -172,7 +173,7 @@ describe('ExecutableGroupNodeChildDTO', () => {
|
||||
|
||||
it('should throw error for group nodes inside subgraphs (unsupported)', () => {
|
||||
// Setup: Group node child inside a subgraph (execution ID has more than 2 segments)
|
||||
const nestedGroupNode = {
|
||||
const nestedGroupNode = createMockLGraphNode({
|
||||
id: '1:2:3', // subgraph:groupnode:innernode
|
||||
graph: {},
|
||||
getInputNode: vi.fn().mockReturnValue(mockInputNode),
|
||||
@@ -180,7 +181,7 @@ describe('ExecutableGroupNodeChildDTO', () => {
|
||||
origin_slot: 0
|
||||
}),
|
||||
inputs: []
|
||||
} as any
|
||||
})
|
||||
|
||||
// Create DTO with deeply nested path to simulate group node inside subgraph
|
||||
const dto = new ExecutableGroupNodeChildDTO(
|
||||
|
||||
Reference in New Issue
Block a user