mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-01-26 19:09:52 +00:00
## Refactor conflict detection system and move to manager extension ### Description This PR refactors the conflict detection system, moving it from the global composables to the manager extension folder for better code organization. Additionally, it improves test type safety and adds comprehensive test coverage for utility functions. ### Main Changes #### 📦 Code Organization - **Moved conflict detection to manager extension** - Relocated all conflict detection related composables, stores, and utilities from global scope to `/workbench/extensions/manager/` for better modularity (https://github.com/Comfy-Org/ComfyUI_frontend/pull/5722) - **Moved from** `src/composables/useConflictDetection.ts` **to** `src/workbench/extensions/manager/composables/useConflictDetection.ts` - Moved related stores and composables to maintain cohesive module structure #### ♻️ Refactoring - **Extracted utility functions** - Split conflict detection logic into separate utility modules: - `conflictUtils.ts` - Conflict consolidation and summary generation - `systemCompatibility.ts` - OS and accelerator compatibility checking - `versionUtil.ts` - Version compatibility checking - **Removed duplicate state management** - Cleaned up redundant state and unused functions - **Improved naming conventions** - Renamed functions for better clarity - **Removed unused system environment code** - Cleaned up deprecated code #### 🔧 Test Improvements - **Fixed TypeScript errors** in all test files - removed all `any` type usage - **Added comprehensive test coverage**: - `conflictUtils.test.ts` - 299 lines of tests for conflict utilities - `systemCompatibility.test.ts` - 270 lines of tests for compatibility checking - `versionUtil.test.ts` - 342 lines of tests for version utilities - **Updated mock objects** to match actual implementations - **Aligned with backend changes** - Updated SystemStats structure to include `pytorch_version`, `embedded_python`, `required_frontend_version` #### 🐛 Bug Fixes - **Fixed OS detection bug** - Resolved issue where 'darwin' was incorrectly matched as 'Windows' due to containing 'win' substring - **Fixed import paths** - Updated all import paths after moving to manager extension - **Fixed unused exports** - Removed all unused function exports - **Fixed lint errors** - Resolved all ESLint and Prettier issues ### File Structure Changes ``` Before: src/ ├── composables/ │ └── useConflictDetection.ts (1374 lines) └── types/ After: src/ ├── utils/ │ ├── conflictUtils.ts (114 lines) │ ├── systemCompatibility.ts (125 lines) │ └── versionUtil.ts (enhanced) └── workbench/extensions/manager/ ├── composables/ │ ├── useConflictDetection.ts (758 lines) │ └── [other composables] └── stores/ └── conflictDetectionStore.ts ``` ### Testing All tests pass successfully: - ✅ **155 test files passed** - ✅ **2209 tests passed** - ⏩ 19 skipped (intentionally skipped subgraph-related tests) ### Impact - **Better code organization** - Manager-specific code is now properly isolated - **Improved maintainability** - Smaller, focused utility functions are easier to test and maintain - **Enhanced type safety** - No more `any` types in tests - **Comprehensive test coverage** - All utility functions are thoroughly tested ### Commits in this PR 1. OS detection bug fix and refactor 2. Remove unused system environment code 3. Improve function naming 4. Refactor conflict detection 5. Remove duplicate state and unused functions 6. Fix unused function exports 7. Move manager features to workbench extension folder 8. Fix import paths 9. Rename systemCompatibility file 10. Improve test type safety 11. Apply ESLint and Prettier fixes ## Screenshots (if applicable) [screen-capture.webm](https://github.com/user-attachments/assets/b4595604-3761-4d98-ae8e-5693cd0c95bd) ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-5436-Manager-refactor-conflict-detect-2686d73d36508186ba06f57dae3656e5) by [Unito](https://www.unito.io) --------- Co-authored-by: Claude <noreply@anthropic.com>
559 lines
19 KiB
TypeScript
559 lines
19 KiB
TypeScript
import { compare, valid } from 'semver'
|
|
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
import { nextTick, ref } from 'vue'
|
|
|
|
import type { components } from '@/types/comfyRegistryTypes'
|
|
import { useInstalledPacks } from '@/workbench/extensions/manager/composables/nodePack/useInstalledPacks'
|
|
import { useUpdateAvailableNodes } from '@/workbench/extensions/manager/composables/nodePack/useUpdateAvailableNodes'
|
|
import { useComfyManagerStore } from '@/workbench/extensions/manager/stores/comfyManagerStore'
|
|
|
|
// Mock Vue's onMounted to execute immediately for testing
|
|
vi.mock('vue', async () => {
|
|
const actual = await vi.importActual('vue')
|
|
return {
|
|
...actual,
|
|
onMounted: (cb: () => void) => cb()
|
|
}
|
|
})
|
|
|
|
// Mock the dependencies
|
|
vi.mock(
|
|
'@/workbench/extensions/manager/composables/nodePack/useInstalledPacks',
|
|
() => ({
|
|
useInstalledPacks: vi.fn()
|
|
})
|
|
)
|
|
|
|
vi.mock('@/workbench/extensions/manager/stores/comfyManagerStore', () => ({
|
|
useComfyManagerStore: vi.fn()
|
|
}))
|
|
|
|
vi.mock('semver', () => ({
|
|
compare: vi.fn(),
|
|
valid: vi.fn()
|
|
}))
|
|
|
|
const mockUseInstalledPacks = vi.mocked(useInstalledPacks)
|
|
const mockUseComfyManagerStore = vi.mocked(useComfyManagerStore)
|
|
|
|
const mockSemverCompare = vi.mocked(compare)
|
|
const mockSemverValid = vi.mocked(valid)
|
|
|
|
describe('useUpdateAvailableNodes', () => {
|
|
const mockInstalledPacks = [
|
|
{
|
|
id: 'pack-1',
|
|
name: 'Outdated Pack',
|
|
latest_version: { version: '2.0.0' }
|
|
} as components['schemas']['Node'],
|
|
{
|
|
id: 'pack-2',
|
|
name: 'Up to Date Pack',
|
|
latest_version: { version: '1.0.0' }
|
|
} as components['schemas']['Node'],
|
|
{
|
|
id: 'pack-3',
|
|
name: 'Nightly Pack',
|
|
latest_version: { version: '1.5.0' }
|
|
} as components['schemas']['Node'],
|
|
{
|
|
id: 'pack-4',
|
|
name: 'No Latest Version',
|
|
latest_version: undefined
|
|
} as components['schemas']['Node']
|
|
]
|
|
|
|
const mockStartFetchInstalled = vi.fn()
|
|
const mockIsPackInstalled = vi.fn()
|
|
const mockGetInstalledPackVersion = vi.fn()
|
|
const mockIsPackEnabled = vi.fn()
|
|
|
|
beforeEach(() => {
|
|
vi.clearAllMocks()
|
|
|
|
// Default setup
|
|
mockIsPackInstalled.mockReturnValue(true)
|
|
mockIsPackEnabled.mockReturnValue(true) // Default: all packs are enabled
|
|
mockGetInstalledPackVersion.mockImplementation((id: string) => {
|
|
switch (id) {
|
|
case 'pack-1':
|
|
return '1.0.0' // outdated
|
|
case 'pack-2':
|
|
return '1.0.0' // up to date
|
|
case 'pack-3':
|
|
return 'nightly-abc123' // nightly
|
|
case 'pack-4':
|
|
return '1.0.0' // no latest version
|
|
default:
|
|
return '1.0.0'
|
|
}
|
|
})
|
|
|
|
mockSemverValid.mockImplementation((version) => {
|
|
return version &&
|
|
typeof version === 'string' &&
|
|
!version.includes('nightly')
|
|
? version
|
|
: null
|
|
})
|
|
|
|
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
|
|
})
|
|
|
|
mockUseComfyManagerStore.mockReturnValue({
|
|
isPackInstalled: mockIsPackInstalled,
|
|
getInstalledPackVersion: mockGetInstalledPackVersion,
|
|
isPackEnabled: mockIsPackEnabled
|
|
} as unknown as ReturnType<typeof useComfyManagerStore>)
|
|
|
|
mockUseInstalledPacks.mockReturnValue({
|
|
installedPacks: ref([]),
|
|
isLoading: ref(false),
|
|
isReady: ref(false),
|
|
error: ref(null),
|
|
startFetchInstalled: mockStartFetchInstalled,
|
|
installedPacksWithVersions: ref([]),
|
|
filterInstalledPack: vi.fn()
|
|
} as unknown as ReturnType<typeof useInstalledPacks>)
|
|
})
|
|
|
|
describe('core filtering logic', () => {
|
|
it('identifies outdated packs correctly', () => {
|
|
mockUseInstalledPacks.mockReturnValue({
|
|
installedPacks: ref(mockInstalledPacks),
|
|
isLoading: ref(false),
|
|
isReady: ref(false),
|
|
error: ref(null),
|
|
startFetchInstalled: mockStartFetchInstalled,
|
|
installedPacksWithVersions: ref([]),
|
|
filterInstalledPack: vi.fn()
|
|
} as unknown as ReturnType<typeof useInstalledPacks>)
|
|
|
|
const { updateAvailableNodePacks } = useUpdateAvailableNodes()
|
|
|
|
// Should only include pack-1 (outdated)
|
|
expect(updateAvailableNodePacks.value).toHaveLength(1)
|
|
expect(updateAvailableNodePacks.value[0].id).toBe('pack-1')
|
|
})
|
|
|
|
it('excludes up-to-date packs', () => {
|
|
mockUseInstalledPacks.mockReturnValue({
|
|
installedPacks: ref([mockInstalledPacks[1]]), // pack-2: up to date
|
|
isLoading: ref(false),
|
|
isReady: ref(false),
|
|
error: ref(null),
|
|
startFetchInstalled: mockStartFetchInstalled,
|
|
installedPacksWithVersions: ref([]),
|
|
filterInstalledPack: vi.fn()
|
|
} as unknown as ReturnType<typeof useInstalledPacks>)
|
|
|
|
const { updateAvailableNodePacks } = useUpdateAvailableNodes()
|
|
|
|
expect(updateAvailableNodePacks.value).toHaveLength(0)
|
|
})
|
|
|
|
it('excludes nightly packs from updates', () => {
|
|
mockUseInstalledPacks.mockReturnValue({
|
|
installedPacks: ref([mockInstalledPacks[2]]), // pack-3: nightly
|
|
isLoading: ref(false),
|
|
isReady: ref(false),
|
|
error: ref(null),
|
|
startFetchInstalled: mockStartFetchInstalled,
|
|
installedPacksWithVersions: ref([]),
|
|
filterInstalledPack: vi.fn()
|
|
} as unknown as ReturnType<typeof useInstalledPacks>)
|
|
|
|
const { updateAvailableNodePacks } = useUpdateAvailableNodes()
|
|
|
|
expect(updateAvailableNodePacks.value).toHaveLength(0)
|
|
})
|
|
|
|
it('excludes packs with no latest version', () => {
|
|
mockUseInstalledPacks.mockReturnValue({
|
|
installedPacks: ref([mockInstalledPacks[3]]), // pack-4: no latest version
|
|
isLoading: ref(false),
|
|
isReady: ref(false),
|
|
error: ref(null),
|
|
startFetchInstalled: mockStartFetchInstalled,
|
|
installedPacksWithVersions: ref([]),
|
|
filterInstalledPack: vi.fn()
|
|
} as unknown as ReturnType<typeof useInstalledPacks>)
|
|
|
|
const { updateAvailableNodePacks } = useUpdateAvailableNodes()
|
|
|
|
expect(updateAvailableNodePacks.value).toHaveLength(0)
|
|
})
|
|
|
|
it('excludes uninstalled packs', () => {
|
|
mockIsPackInstalled.mockReturnValue(false)
|
|
mockUseInstalledPacks.mockReturnValue({
|
|
installedPacks: ref(mockInstalledPacks),
|
|
isLoading: ref(false),
|
|
isReady: ref(false),
|
|
error: ref(null),
|
|
startFetchInstalled: mockStartFetchInstalled,
|
|
installedPacksWithVersions: ref([]),
|
|
filterInstalledPack: vi.fn()
|
|
} as unknown as ReturnType<typeof useInstalledPacks>)
|
|
|
|
const { updateAvailableNodePacks } = useUpdateAvailableNodes()
|
|
|
|
expect(updateAvailableNodePacks.value).toHaveLength(0)
|
|
})
|
|
|
|
it('returns empty array when no installed packs exist', () => {
|
|
const { updateAvailableNodePacks } = useUpdateAvailableNodes()
|
|
|
|
expect(updateAvailableNodePacks.value).toEqual([])
|
|
})
|
|
})
|
|
|
|
describe('hasUpdateAvailable computed', () => {
|
|
it('returns true when updates are available', () => {
|
|
mockUseInstalledPacks.mockReturnValue({
|
|
installedPacks: ref([mockInstalledPacks[0]]), // pack-1: outdated
|
|
isLoading: ref(false),
|
|
error: ref(null),
|
|
startFetchInstalled: mockStartFetchInstalled,
|
|
isReady: ref(false),
|
|
installedPacksWithVersions: ref([]),
|
|
filterInstalledPack: vi.fn()
|
|
} as unknown as ReturnType<typeof useInstalledPacks>)
|
|
|
|
const { hasUpdateAvailable } = useUpdateAvailableNodes()
|
|
|
|
expect(hasUpdateAvailable.value).toBe(true)
|
|
})
|
|
|
|
it('returns false when no updates are available', () => {
|
|
mockUseInstalledPacks.mockReturnValue({
|
|
installedPacks: ref([mockInstalledPacks[1]]), // pack-2: up to date
|
|
isLoading: ref(false),
|
|
isReady: ref(false),
|
|
error: ref(null),
|
|
startFetchInstalled: mockStartFetchInstalled,
|
|
installedPacksWithVersions: ref([]),
|
|
filterInstalledPack: vi.fn()
|
|
} as unknown as ReturnType<typeof useInstalledPacks>)
|
|
|
|
const { hasUpdateAvailable } = useUpdateAvailableNodes()
|
|
|
|
expect(hasUpdateAvailable.value).toBe(false)
|
|
})
|
|
})
|
|
|
|
describe('automatic data fetching', () => {
|
|
it('fetches installed packs automatically when none exist', () => {
|
|
useUpdateAvailableNodes()
|
|
|
|
expect(mockStartFetchInstalled).toHaveBeenCalledOnce()
|
|
})
|
|
|
|
it('does not fetch when packs already exist', () => {
|
|
mockUseInstalledPacks.mockReturnValue({
|
|
installedPacks: ref(mockInstalledPacks),
|
|
isLoading: ref(false),
|
|
isReady: ref(false),
|
|
error: ref(null),
|
|
startFetchInstalled: mockStartFetchInstalled,
|
|
installedPacksWithVersions: ref([]),
|
|
filterInstalledPack: vi.fn()
|
|
} as unknown as ReturnType<typeof useInstalledPacks>)
|
|
|
|
useUpdateAvailableNodes()
|
|
|
|
expect(mockStartFetchInstalled).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it('does not fetch when already loading', () => {
|
|
mockUseInstalledPacks.mockReturnValue({
|
|
installedPacks: ref([]),
|
|
isLoading: ref(true),
|
|
error: ref(null),
|
|
startFetchInstalled: mockStartFetchInstalled,
|
|
isReady: ref(false),
|
|
installedPacksWithVersions: ref([]),
|
|
filterInstalledPack: vi.fn()
|
|
} as unknown as ReturnType<typeof useInstalledPacks>)
|
|
|
|
useUpdateAvailableNodes()
|
|
|
|
expect(mockStartFetchInstalled).not.toHaveBeenCalled()
|
|
})
|
|
})
|
|
|
|
describe('state management', () => {
|
|
it('exposes loading state from useInstalledPacks', () => {
|
|
mockUseInstalledPacks.mockReturnValue({
|
|
installedPacks: ref([]),
|
|
isLoading: ref(true),
|
|
error: ref(null),
|
|
startFetchInstalled: mockStartFetchInstalled,
|
|
isReady: ref(false),
|
|
installedPacksWithVersions: ref([]),
|
|
filterInstalledPack: vi.fn()
|
|
} as unknown as ReturnType<typeof useInstalledPacks>)
|
|
|
|
const { isLoading } = useUpdateAvailableNodes()
|
|
|
|
expect(isLoading.value).toBe(true)
|
|
})
|
|
|
|
it('exposes error state from useInstalledPacks', () => {
|
|
const testError = 'Failed to fetch installed packs'
|
|
mockUseInstalledPacks.mockReturnValue({
|
|
installedPacks: ref([]),
|
|
isLoading: ref(false),
|
|
error: ref(testError),
|
|
startFetchInstalled: mockStartFetchInstalled,
|
|
isReady: ref(false),
|
|
installedPacksWithVersions: ref([]),
|
|
filterInstalledPack: vi.fn()
|
|
} as unknown as ReturnType<typeof useInstalledPacks>)
|
|
|
|
const { error } = useUpdateAvailableNodes()
|
|
|
|
expect(error.value).toBe(testError)
|
|
})
|
|
})
|
|
|
|
describe('reactivity', () => {
|
|
it('updates when installed packs change', async () => {
|
|
const installedPacksRef = ref<components['schemas']['Node'][]>([])
|
|
mockUseInstalledPacks.mockReturnValue({
|
|
installedPacks: installedPacksRef,
|
|
isLoading: ref(false),
|
|
error: ref(null),
|
|
startFetchInstalled: mockStartFetchInstalled,
|
|
isReady: ref(false),
|
|
installedPacksWithVersions: ref([]),
|
|
filterInstalledPack: vi.fn()
|
|
} as unknown as ReturnType<typeof useInstalledPacks>)
|
|
|
|
const { updateAvailableNodePacks, hasUpdateAvailable } =
|
|
useUpdateAvailableNodes()
|
|
|
|
// Initially empty
|
|
expect(updateAvailableNodePacks.value).toEqual([])
|
|
expect(hasUpdateAvailable.value).toBe(false)
|
|
|
|
// Update installed packs
|
|
installedPacksRef.value = [mockInstalledPacks[0]]
|
|
await nextTick()
|
|
|
|
// Should update available updates
|
|
expect(updateAvailableNodePacks.value).toHaveLength(1)
|
|
expect(hasUpdateAvailable.value).toBe(true)
|
|
})
|
|
})
|
|
|
|
describe('version comparison logic', () => {
|
|
it('calls compareVersions with correct parameters', () => {
|
|
mockUseInstalledPacks.mockReturnValue({
|
|
installedPacks: ref([mockInstalledPacks[0]]), // pack-1
|
|
isLoading: ref(false),
|
|
error: ref(null),
|
|
startFetchInstalled: mockStartFetchInstalled,
|
|
isReady: ref(false),
|
|
installedPacksWithVersions: ref([]),
|
|
filterInstalledPack: vi.fn()
|
|
} as unknown as ReturnType<typeof useInstalledPacks>)
|
|
|
|
const { updateAvailableNodePacks } = useUpdateAvailableNodes()
|
|
|
|
// Access the computed to trigger the logic
|
|
expect(updateAvailableNodePacks.value).toBeDefined()
|
|
|
|
expect(mockSemverCompare).toHaveBeenCalledWith('2.0.0', '1.0.0')
|
|
})
|
|
|
|
it('calls semver.valid to check nightly versions', () => {
|
|
mockUseInstalledPacks.mockReturnValue({
|
|
installedPacks: ref([mockInstalledPacks[2]]), // pack-3: nightly
|
|
isLoading: ref(false),
|
|
isReady: ref(false),
|
|
error: ref(null),
|
|
startFetchInstalled: mockStartFetchInstalled,
|
|
installedPacksWithVersions: ref([]),
|
|
filterInstalledPack: vi.fn()
|
|
} as unknown as ReturnType<typeof useInstalledPacks>)
|
|
|
|
const { updateAvailableNodePacks } = useUpdateAvailableNodes()
|
|
|
|
// Access the computed to trigger the logic
|
|
expect(updateAvailableNodePacks.value).toBeDefined()
|
|
|
|
expect(mockSemverValid).toHaveBeenCalledWith('nightly-abc123')
|
|
})
|
|
|
|
it('calls isPackInstalled for each pack', () => {
|
|
mockUseInstalledPacks.mockReturnValue({
|
|
installedPacks: ref(mockInstalledPacks),
|
|
isLoading: ref(false),
|
|
isReady: ref(false),
|
|
error: ref(null),
|
|
startFetchInstalled: mockStartFetchInstalled,
|
|
installedPacksWithVersions: ref([]),
|
|
filterInstalledPack: vi.fn()
|
|
} as unknown as ReturnType<typeof useInstalledPacks>)
|
|
|
|
const { updateAvailableNodePacks } = useUpdateAvailableNodes()
|
|
|
|
// Access the computed to trigger the logic
|
|
expect(updateAvailableNodePacks.value).toBeDefined()
|
|
|
|
expect(mockIsPackInstalled).toHaveBeenCalledWith('pack-1')
|
|
expect(mockIsPackInstalled).toHaveBeenCalledWith('pack-2')
|
|
expect(mockIsPackInstalled).toHaveBeenCalledWith('pack-3')
|
|
expect(mockIsPackInstalled).toHaveBeenCalledWith('pack-4')
|
|
})
|
|
})
|
|
|
|
describe('enabledUpdateAvailableNodePacks', () => {
|
|
it('returns only enabled packs with updates', () => {
|
|
mockIsPackEnabled.mockImplementation((id: string) => {
|
|
// pack-1 is disabled
|
|
return id !== 'pack-1'
|
|
})
|
|
|
|
mockUseInstalledPacks.mockReturnValue({
|
|
installedPacks: ref([mockInstalledPacks[0], mockInstalledPacks[1]]),
|
|
isLoading: ref(false),
|
|
error: ref(null),
|
|
startFetchInstalled: mockStartFetchInstalled,
|
|
isReady: ref(false),
|
|
installedPacksWithVersions: ref([]),
|
|
filterInstalledPack: vi.fn()
|
|
} as unknown as ReturnType<typeof useInstalledPacks>)
|
|
|
|
const { updateAvailableNodePacks, enabledUpdateAvailableNodePacks } =
|
|
useUpdateAvailableNodes()
|
|
|
|
// pack-1 has updates but is disabled
|
|
expect(updateAvailableNodePacks.value).toHaveLength(1)
|
|
expect(updateAvailableNodePacks.value[0].id).toBe('pack-1')
|
|
|
|
// enabledUpdateAvailableNodePacks should be empty
|
|
expect(enabledUpdateAvailableNodePacks.value).toHaveLength(0)
|
|
})
|
|
|
|
it('returns all packs when all are enabled', () => {
|
|
mockUseInstalledPacks.mockReturnValue({
|
|
installedPacks: ref([mockInstalledPacks[0]]), // pack-1: outdated
|
|
isLoading: ref(false),
|
|
error: ref(null),
|
|
startFetchInstalled: mockStartFetchInstalled,
|
|
isReady: ref(false),
|
|
installedPacksWithVersions: ref([]),
|
|
filterInstalledPack: vi.fn()
|
|
} as unknown as ReturnType<typeof useInstalledPacks>)
|
|
|
|
const { updateAvailableNodePacks, enabledUpdateAvailableNodePacks } =
|
|
useUpdateAvailableNodes()
|
|
|
|
expect(updateAvailableNodePacks.value).toHaveLength(1)
|
|
expect(enabledUpdateAvailableNodePacks.value).toHaveLength(1)
|
|
expect(enabledUpdateAvailableNodePacks.value[0].id).toBe('pack-1')
|
|
})
|
|
})
|
|
|
|
describe('hasDisabledUpdatePacks', () => {
|
|
it('returns true when there are disabled packs with updates', () => {
|
|
mockIsPackEnabled.mockImplementation((id: string) => {
|
|
// pack-1 is disabled
|
|
return id !== 'pack-1'
|
|
})
|
|
|
|
mockUseInstalledPacks.mockReturnValue({
|
|
installedPacks: ref([mockInstalledPacks[0]]), // pack-1: outdated
|
|
isLoading: ref(false),
|
|
error: ref(null),
|
|
startFetchInstalled: mockStartFetchInstalled,
|
|
isReady: ref(false),
|
|
installedPacksWithVersions: ref([]),
|
|
filterInstalledPack: vi.fn()
|
|
} as unknown as ReturnType<typeof useInstalledPacks>)
|
|
|
|
const { hasDisabledUpdatePacks } = useUpdateAvailableNodes()
|
|
|
|
expect(hasDisabledUpdatePacks.value).toBe(true)
|
|
})
|
|
|
|
it('returns false when all packs with updates are enabled', () => {
|
|
mockUseInstalledPacks.mockReturnValue({
|
|
installedPacks: ref([mockInstalledPacks[0]]), // pack-1: outdated
|
|
isLoading: ref(false),
|
|
error: ref(null),
|
|
startFetchInstalled: mockStartFetchInstalled,
|
|
isReady: ref(false),
|
|
installedPacksWithVersions: ref([]),
|
|
filterInstalledPack: vi.fn()
|
|
} as unknown as ReturnType<typeof useInstalledPacks>)
|
|
|
|
const { hasDisabledUpdatePacks } = useUpdateAvailableNodes()
|
|
|
|
expect(hasDisabledUpdatePacks.value).toBe(false)
|
|
})
|
|
|
|
it('returns false when no packs have updates', () => {
|
|
mockUseInstalledPacks.mockReturnValue({
|
|
installedPacks: ref([mockInstalledPacks[1]]), // pack-2: up to date
|
|
isLoading: ref(false),
|
|
isReady: ref(false),
|
|
error: ref(null),
|
|
startFetchInstalled: mockStartFetchInstalled,
|
|
installedPacksWithVersions: ref([]),
|
|
filterInstalledPack: vi.fn()
|
|
} as unknown as ReturnType<typeof useInstalledPacks>)
|
|
|
|
const { hasDisabledUpdatePacks } = useUpdateAvailableNodes()
|
|
|
|
expect(hasDisabledUpdatePacks.value).toBe(false)
|
|
})
|
|
})
|
|
|
|
describe('hasUpdateAvailable with disabled packs', () => {
|
|
it('returns false when only disabled packs have updates', () => {
|
|
mockIsPackEnabled.mockReturnValue(false) // All packs disabled
|
|
|
|
mockUseInstalledPacks.mockReturnValue({
|
|
installedPacks: ref([mockInstalledPacks[0]]), // pack-1: outdated
|
|
isLoading: ref(false),
|
|
error: ref(null),
|
|
startFetchInstalled: mockStartFetchInstalled,
|
|
isReady: ref(false),
|
|
installedPacksWithVersions: ref([]),
|
|
filterInstalledPack: vi.fn()
|
|
} as unknown as ReturnType<typeof useInstalledPacks>)
|
|
|
|
const { hasUpdateAvailable } = useUpdateAvailableNodes()
|
|
|
|
expect(hasUpdateAvailable.value).toBe(false)
|
|
})
|
|
|
|
it('returns true when at least one enabled pack has updates', () => {
|
|
mockIsPackEnabled.mockImplementation((id: string) => {
|
|
// Only pack-1 is enabled
|
|
return id === 'pack-1'
|
|
})
|
|
|
|
mockUseInstalledPacks.mockReturnValue({
|
|
installedPacks: ref([mockInstalledPacks[0]]), // pack-1: outdated
|
|
isLoading: ref(false),
|
|
error: ref(null),
|
|
startFetchInstalled: mockStartFetchInstalled,
|
|
isReady: ref(false),
|
|
installedPacksWithVersions: ref([]),
|
|
filterInstalledPack: vi.fn()
|
|
} as unknown as ReturnType<typeof useInstalledPacks>)
|
|
|
|
const { hasUpdateAvailable } = useUpdateAvailableNodes()
|
|
|
|
expect(hasUpdateAvailable.value).toBe(true)
|
|
})
|
|
})
|
|
})
|