mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-29 18:52:19 +00:00
[restore] conflict notification commits restore
This commit is contained in:
119
tests-ui/tests/composables/useConflictAcknowledgment.spec.ts
Normal file
119
tests-ui/tests/composables/useConflictAcknowledgment.spec.ts
Normal file
@@ -0,0 +1,119 @@
|
||||
import { createPinia, setActivePinia } from 'pinia'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import { useConflictAcknowledgment } from '@/composables/useConflictAcknowledgment'
|
||||
|
||||
describe('useConflictAcknowledgment with useStorage refactor', () => {
|
||||
beforeEach(() => {
|
||||
// Set up Pinia for each test
|
||||
setActivePinia(createPinia())
|
||||
// Clear localStorage before each test
|
||||
localStorage.clear()
|
||||
// Reset modules to ensure fresh state
|
||||
vi.resetModules()
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
localStorage.clear()
|
||||
})
|
||||
|
||||
it('should initialize with default values', () => {
|
||||
const {
|
||||
shouldShowConflictModal,
|
||||
shouldShowRedDot,
|
||||
shouldShowManagerBanner
|
||||
} = useConflictAcknowledgment()
|
||||
|
||||
expect(shouldShowConflictModal.value).toBe(true)
|
||||
expect(shouldShowRedDot.value).toBe(false) // No conflicts initially
|
||||
expect(shouldShowManagerBanner.value).toBe(false) // No conflicts initially
|
||||
})
|
||||
|
||||
it('should dismiss modal state correctly', () => {
|
||||
const { markConflictsAsSeen, shouldShowConflictModal } =
|
||||
useConflictAcknowledgment()
|
||||
|
||||
expect(shouldShowConflictModal.value).toBe(true)
|
||||
markConflictsAsSeen()
|
||||
expect(shouldShowConflictModal.value).toBe(false)
|
||||
})
|
||||
|
||||
it('should dismiss red dot notification correctly', () => {
|
||||
const { dismissRedDotNotification, shouldShowRedDot } =
|
||||
useConflictAcknowledgment()
|
||||
|
||||
expect(shouldShowRedDot.value).toBe(false) // No conflicts initially
|
||||
dismissRedDotNotification()
|
||||
expect(shouldShowRedDot.value).toBe(false)
|
||||
})
|
||||
|
||||
it('should dismiss warning banner correctly', () => {
|
||||
const { dismissWarningBanner, shouldShowManagerBanner } =
|
||||
useConflictAcknowledgment()
|
||||
|
||||
// Initially should not show banner (no conflicts)
|
||||
expect(shouldShowManagerBanner.value).toBe(false)
|
||||
|
||||
// Test dismissWarningBanner function exists and works
|
||||
dismissWarningBanner()
|
||||
expect(shouldShowManagerBanner.value).toBe(false)
|
||||
})
|
||||
|
||||
it('should mark conflicts as seen', () => {
|
||||
const {
|
||||
markConflictsAsSeen,
|
||||
shouldShowConflictModal,
|
||||
shouldShowRedDot,
|
||||
shouldShowManagerBanner
|
||||
} = useConflictAcknowledgment()
|
||||
|
||||
// Mark conflicts as seen
|
||||
markConflictsAsSeen()
|
||||
|
||||
// All UI elements should be dismissed
|
||||
expect(shouldShowConflictModal.value).toBe(false)
|
||||
expect(shouldShowRedDot.value).toBe(false)
|
||||
expect(shouldShowManagerBanner.value).toBe(false)
|
||||
})
|
||||
|
||||
it('should manage acknowledgment state correctly', () => {
|
||||
const {
|
||||
acknowledgmentState,
|
||||
markConflictsAsSeen,
|
||||
dismissRedDotNotification,
|
||||
dismissWarningBanner
|
||||
} = useConflictAcknowledgment()
|
||||
|
||||
// Initial state
|
||||
expect(acknowledgmentState.value.modal_dismissed).toBe(false)
|
||||
expect(acknowledgmentState.value.red_dot_dismissed).toBe(false)
|
||||
expect(acknowledgmentState.value.warning_banner_dismissed).toBe(false)
|
||||
|
||||
// Update states
|
||||
markConflictsAsSeen()
|
||||
dismissRedDotNotification()
|
||||
dismissWarningBanner()
|
||||
|
||||
// Check updated state
|
||||
expect(acknowledgmentState.value.modal_dismissed).toBe(true)
|
||||
expect(acknowledgmentState.value.red_dot_dismissed).toBe(true)
|
||||
expect(acknowledgmentState.value.warning_banner_dismissed).toBe(true)
|
||||
})
|
||||
|
||||
it('should use VueUse useStorage for persistence', () => {
|
||||
// This test verifies that useStorage is being used by checking
|
||||
// that values are automatically synced to localStorage
|
||||
const { markConflictsAsSeen, dismissWarningBanner } =
|
||||
useConflictAcknowledgment()
|
||||
|
||||
markConflictsAsSeen()
|
||||
dismissWarningBanner()
|
||||
|
||||
// VueUse useStorage should automatically persist to localStorage
|
||||
// We can verify the keys exist (values will be stringified by VueUse)
|
||||
expect(localStorage.getItem('Comfy.ConflictModalDismissed')).not.toBeNull()
|
||||
expect(
|
||||
localStorage.getItem('Comfy.ConflictWarningBannerDismissed')
|
||||
).not.toBeNull()
|
||||
})
|
||||
})
|
||||
138
tests-ui/tests/composables/useConflictAcknowledgment.test.ts
Normal file
138
tests-ui/tests/composables/useConflictAcknowledgment.test.ts
Normal file
@@ -0,0 +1,138 @@
|
||||
import { createPinia, setActivePinia } from 'pinia'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import { useConflictAcknowledgment } from '@/composables/useConflictAcknowledgment'
|
||||
|
||||
describe('useConflictAcknowledgment', () => {
|
||||
beforeEach(() => {
|
||||
// Set up Pinia for each test
|
||||
setActivePinia(createPinia())
|
||||
// Clear localStorage before each test
|
||||
localStorage.clear()
|
||||
// Reset modules to ensure fresh state
|
||||
vi.resetModules()
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
localStorage.clear()
|
||||
})
|
||||
|
||||
describe('initial state loading', () => {
|
||||
it('should load empty state when localStorage is empty', () => {
|
||||
const { acknowledgmentState } = useConflictAcknowledgment()
|
||||
|
||||
expect(acknowledgmentState.value).toEqual({
|
||||
modal_dismissed: false,
|
||||
red_dot_dismissed: false,
|
||||
warning_banner_dismissed: false
|
||||
})
|
||||
})
|
||||
|
||||
it('should load existing state from localStorage', () => {
|
||||
// Pre-populate localStorage
|
||||
localStorage.setItem('Comfy.ConflictModalDismissed', 'true')
|
||||
localStorage.setItem('Comfy.ConflictRedDotDismissed', 'true')
|
||||
localStorage.setItem('Comfy.ConflictWarningBannerDismissed', 'true')
|
||||
|
||||
const { acknowledgmentState } = useConflictAcknowledgment()
|
||||
|
||||
expect(acknowledgmentState.value).toEqual({
|
||||
modal_dismissed: true,
|
||||
red_dot_dismissed: true,
|
||||
warning_banner_dismissed: true
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('dismissal functions', () => {
|
||||
it('should mark conflicts as seen with unified function', () => {
|
||||
const { markConflictsAsSeen, acknowledgmentState } =
|
||||
useConflictAcknowledgment()
|
||||
|
||||
markConflictsAsSeen()
|
||||
|
||||
expect(acknowledgmentState.value.modal_dismissed).toBe(true)
|
||||
})
|
||||
|
||||
it('should dismiss red dot notification', () => {
|
||||
const { dismissRedDotNotification, acknowledgmentState } =
|
||||
useConflictAcknowledgment()
|
||||
|
||||
dismissRedDotNotification()
|
||||
|
||||
expect(acknowledgmentState.value.red_dot_dismissed).toBe(true)
|
||||
})
|
||||
|
||||
it('should dismiss warning banner', () => {
|
||||
const { dismissWarningBanner, acknowledgmentState } =
|
||||
useConflictAcknowledgment()
|
||||
|
||||
dismissWarningBanner()
|
||||
|
||||
expect(acknowledgmentState.value.warning_banner_dismissed).toBe(true)
|
||||
})
|
||||
|
||||
it('should mark all conflicts as seen', () => {
|
||||
const { markConflictsAsSeen, acknowledgmentState } =
|
||||
useConflictAcknowledgment()
|
||||
|
||||
markConflictsAsSeen()
|
||||
|
||||
expect(acknowledgmentState.value.modal_dismissed).toBe(true)
|
||||
expect(acknowledgmentState.value.red_dot_dismissed).toBe(true)
|
||||
expect(acknowledgmentState.value.warning_banner_dismissed).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('computed properties', () => {
|
||||
it('should calculate shouldShowConflictModal correctly', () => {
|
||||
const { shouldShowConflictModal, markConflictsAsSeen } =
|
||||
useConflictAcknowledgment()
|
||||
|
||||
expect(shouldShowConflictModal.value).toBe(true)
|
||||
|
||||
markConflictsAsSeen()
|
||||
expect(shouldShowConflictModal.value).toBe(false)
|
||||
})
|
||||
|
||||
it('should calculate shouldShowRedDot correctly based on conflicts', () => {
|
||||
const { shouldShowRedDot, dismissRedDotNotification } =
|
||||
useConflictAcknowledgment()
|
||||
|
||||
// Initially false because no conflicts exist
|
||||
expect(shouldShowRedDot.value).toBe(false)
|
||||
|
||||
dismissRedDotNotification()
|
||||
expect(shouldShowRedDot.value).toBe(false)
|
||||
})
|
||||
|
||||
it('should calculate shouldShowManagerBanner correctly', () => {
|
||||
const { shouldShowManagerBanner, dismissWarningBanner } =
|
||||
useConflictAcknowledgment()
|
||||
|
||||
// Initially false because no conflicts exist
|
||||
expect(shouldShowManagerBanner.value).toBe(false)
|
||||
|
||||
dismissWarningBanner()
|
||||
expect(shouldShowManagerBanner.value).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('localStorage persistence', () => {
|
||||
it('should persist to localStorage automatically', () => {
|
||||
const { markConflictsAsSeen, dismissWarningBanner } =
|
||||
useConflictAcknowledgment()
|
||||
|
||||
markConflictsAsSeen()
|
||||
dismissWarningBanner()
|
||||
|
||||
// VueUse useStorage should automatically persist to localStorage
|
||||
expect(
|
||||
localStorage.getItem('Comfy.ConflictModalDismissed')
|
||||
).not.toBeNull()
|
||||
expect(
|
||||
localStorage.getItem('Comfy.ConflictWarningBannerDismissed')
|
||||
).not.toBeNull()
|
||||
})
|
||||
})
|
||||
})
|
||||
1015
tests-ui/tests/composables/useConflictDetection.test.ts
Normal file
1015
tests-ui/tests/composables/useConflictDetection.test.ts
Normal file
File diff suppressed because it is too large
Load Diff
198
tests-ui/tests/composables/useImportFailedDetection.test.ts
Normal file
198
tests-ui/tests/composables/useImportFailedDetection.test.ts
Normal file
@@ -0,0 +1,198 @@
|
||||
import { createPinia, setActivePinia } from 'pinia'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
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'
|
||||
|
||||
// Mock the stores and services
|
||||
vi.mock('@/stores/comfyManagerStore')
|
||||
vi.mock('@/stores/conflictDetectionStore')
|
||||
vi.mock('@/services/dialogService')
|
||||
vi.mock('vue-i18n', async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import('vue-i18n')>()
|
||||
return {
|
||||
...actual,
|
||||
useI18n: () => ({
|
||||
t: vi.fn((key: string) => key)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
describe('useImportFailedDetection', () => {
|
||||
let mockComfyManagerStore: any
|
||||
let mockConflictDetectionStore: any
|
||||
let mockDialogService: any
|
||||
|
||||
beforeEach(() => {
|
||||
setActivePinia(createPinia())
|
||||
|
||||
mockComfyManagerStore = {
|
||||
isPackInstalled: vi.fn()
|
||||
}
|
||||
mockConflictDetectionStore = {
|
||||
getConflictsForPackageByID: vi.fn()
|
||||
}
|
||||
mockDialogService = {
|
||||
showErrorDialog: vi.fn()
|
||||
}
|
||||
|
||||
vi.mocked(comfyManagerStore.useComfyManagerStore).mockReturnValue(
|
||||
mockComfyManagerStore
|
||||
)
|
||||
vi.mocked(conflictDetectionStore.useConflictDetectionStore).mockReturnValue(
|
||||
mockConflictDetectionStore
|
||||
)
|
||||
vi.mocked(dialogService.useDialogService).mockReturnValue(mockDialogService)
|
||||
})
|
||||
|
||||
it('should return false for importFailed when package is not installed', () => {
|
||||
mockComfyManagerStore.isPackInstalled.mockReturnValue(false)
|
||||
|
||||
const { importFailed } = useImportFailedDetection('test-package')
|
||||
|
||||
expect(importFailed.value).toBe(false)
|
||||
})
|
||||
|
||||
it('should return false for importFailed when no conflicts exist', () => {
|
||||
mockComfyManagerStore.isPackInstalled.mockReturnValue(true)
|
||||
mockConflictDetectionStore.getConflictsForPackageByID.mockReturnValue(null)
|
||||
|
||||
const { importFailed } = useImportFailedDetection('test-package')
|
||||
|
||||
expect(importFailed.value).toBe(false)
|
||||
})
|
||||
|
||||
it('should return false for importFailed when conflicts exist but no import_failed type', () => {
|
||||
mockComfyManagerStore.isPackInstalled.mockReturnValue(true)
|
||||
mockConflictDetectionStore.getConflictsForPackageByID.mockReturnValue({
|
||||
package_id: 'test-package',
|
||||
conflicts: [
|
||||
{ type: 'dependency', message: 'Dependency conflict' },
|
||||
{ type: 'version', message: 'Version conflict' }
|
||||
]
|
||||
})
|
||||
|
||||
const { importFailed } = useImportFailedDetection('test-package')
|
||||
|
||||
expect(importFailed.value).toBe(false)
|
||||
})
|
||||
|
||||
it('should return true for importFailed when import_failed conflicts exist', () => {
|
||||
mockComfyManagerStore.isPackInstalled.mockReturnValue(true)
|
||||
mockConflictDetectionStore.getConflictsForPackageByID.mockReturnValue({
|
||||
package_id: 'test-package',
|
||||
conflicts: [
|
||||
{
|
||||
type: 'import_failed',
|
||||
message: 'Import failed',
|
||||
required_value: 'Error details'
|
||||
},
|
||||
{ type: 'dependency', message: 'Dependency conflict' }
|
||||
]
|
||||
})
|
||||
|
||||
const { importFailed } = useImportFailedDetection('test-package')
|
||||
|
||||
expect(importFailed.value).toBe(true)
|
||||
})
|
||||
|
||||
it('should work with computed ref packageId', () => {
|
||||
const packageId = ref('test-package')
|
||||
mockComfyManagerStore.isPackInstalled.mockReturnValue(true)
|
||||
mockConflictDetectionStore.getConflictsForPackageByID.mockReturnValue({
|
||||
package_id: 'test-package',
|
||||
conflicts: [
|
||||
{
|
||||
type: 'import_failed',
|
||||
message: 'Import failed',
|
||||
required_value: 'Error details'
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
const { importFailed } = useImportFailedDetection(
|
||||
computed(() => packageId.value)
|
||||
)
|
||||
|
||||
expect(importFailed.value).toBe(true)
|
||||
|
||||
// Change packageId
|
||||
packageId.value = 'another-package'
|
||||
mockConflictDetectionStore.getConflictsForPackageByID.mockReturnValue(null)
|
||||
|
||||
expect(importFailed.value).toBe(false)
|
||||
})
|
||||
|
||||
it('should return correct importFailedInfo', () => {
|
||||
const importFailedConflicts = [
|
||||
{
|
||||
type: 'import_failed',
|
||||
message: 'Import failed 1',
|
||||
required_value: 'Error 1'
|
||||
},
|
||||
{
|
||||
type: 'import_failed',
|
||||
message: 'Import failed 2',
|
||||
required_value: 'Error 2'
|
||||
}
|
||||
]
|
||||
|
||||
mockComfyManagerStore.isPackInstalled.mockReturnValue(true)
|
||||
mockConflictDetectionStore.getConflictsForPackageByID.mockReturnValue({
|
||||
package_id: 'test-package',
|
||||
conflicts: [
|
||||
...importFailedConflicts,
|
||||
{ type: 'dependency', message: 'Dependency conflict' }
|
||||
]
|
||||
})
|
||||
|
||||
const { importFailedInfo } = useImportFailedDetection('test-package')
|
||||
|
||||
expect(importFailedInfo.value).toEqual(importFailedConflicts)
|
||||
})
|
||||
|
||||
it('should show error dialog when showImportFailedDialog is called', () => {
|
||||
const importFailedConflicts = [
|
||||
{
|
||||
type: 'import_failed',
|
||||
message: 'Import failed',
|
||||
required_value: 'Error details'
|
||||
}
|
||||
]
|
||||
|
||||
mockComfyManagerStore.isPackInstalled.mockReturnValue(true)
|
||||
mockConflictDetectionStore.getConflictsForPackageByID.mockReturnValue({
|
||||
package_id: 'test-package',
|
||||
conflicts: importFailedConflicts
|
||||
})
|
||||
|
||||
const { showImportFailedDialog } = useImportFailedDetection('test-package')
|
||||
|
||||
showImportFailedDialog()
|
||||
|
||||
expect(mockDialogService.showErrorDialog).toHaveBeenCalledWith(
|
||||
expect.any(Error),
|
||||
{
|
||||
title: 'manager.failedToInstall',
|
||||
reportType: 'importFailedError'
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
it('should handle null packageId', () => {
|
||||
const { importFailed, isInstalled } = useImportFailedDetection(null)
|
||||
|
||||
expect(importFailed.value).toBe(false)
|
||||
expect(isInstalled.value).toBe(false)
|
||||
})
|
||||
|
||||
it('should handle undefined packageId', () => {
|
||||
const { importFailed, isInstalled } = useImportFailedDetection(undefined)
|
||||
|
||||
expect(importFailed.value).toBe(false)
|
||||
expect(isInstalled.value).toBe(false)
|
||||
})
|
||||
})
|
||||
360
tests-ui/tests/composables/useUpdateAvailableNodes.test.ts
Normal file
360
tests-ui/tests/composables/useUpdateAvailableNodes.test.ts
Normal file
@@ -0,0 +1,360 @@
|
||||
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 '@/stores/comfyManagerStore'
|
||||
// Import mocked utils
|
||||
import { compareVersions, isSemVer } from '@/utils/formatUtil'
|
||||
|
||||
// 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('@/composables/nodePack/useInstalledPacks', () => ({
|
||||
useInstalledPacks: vi.fn()
|
||||
}))
|
||||
|
||||
vi.mock('@/stores/comfyManagerStore', () => ({
|
||||
useComfyManagerStore: vi.fn()
|
||||
}))
|
||||
|
||||
vi.mock('@/utils/formatUtil', () => ({
|
||||
compareVersions: vi.fn(),
|
||||
isSemVer: vi.fn()
|
||||
}))
|
||||
|
||||
const mockUseInstalledPacks = vi.mocked(useInstalledPacks)
|
||||
const mockUseComfyManagerStore = vi.mocked(useComfyManagerStore)
|
||||
|
||||
const mockCompareVersions = vi.mocked(compareVersions)
|
||||
const mockIsSemVer = vi.mocked(isSemVer)
|
||||
|
||||
describe('useUpdateAvailableNodes', () => {
|
||||
const mockInstalledPacks = [
|
||||
{
|
||||
id: 'pack-1',
|
||||
name: 'Outdated Pack',
|
||||
latest_version: { version: '2.0.0' }
|
||||
},
|
||||
{
|
||||
id: 'pack-2',
|
||||
name: 'Up to Date Pack',
|
||||
latest_version: { version: '1.0.0' }
|
||||
},
|
||||
{
|
||||
id: 'pack-3',
|
||||
name: 'Nightly Pack',
|
||||
latest_version: { version: '1.5.0' }
|
||||
},
|
||||
{
|
||||
id: 'pack-4',
|
||||
name: 'No Latest Version',
|
||||
latest_version: null
|
||||
}
|
||||
]
|
||||
|
||||
const mockStartFetchInstalled = vi.fn()
|
||||
const mockIsPackInstalled = vi.fn()
|
||||
const mockGetInstalledPackVersion = vi.fn()
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
|
||||
// Default setup
|
||||
mockIsPackInstalled.mockReturnValue(true)
|
||||
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'
|
||||
}
|
||||
})
|
||||
|
||||
mockIsSemVer.mockImplementation(
|
||||
(version: string): version is `${number}.${number}.${number}` => {
|
||||
return !version.includes('nightly')
|
||||
}
|
||||
)
|
||||
|
||||
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,
|
||||
getInstalledPackVersion: mockGetInstalledPackVersion
|
||||
} as any)
|
||||
|
||||
mockUseInstalledPacks.mockReturnValue({
|
||||
installedPacks: ref([]),
|
||||
isLoading: ref(false),
|
||||
error: ref(null),
|
||||
startFetchInstalled: mockStartFetchInstalled
|
||||
} as any)
|
||||
})
|
||||
|
||||
describe('core filtering logic', () => {
|
||||
it('identifies outdated packs correctly', () => {
|
||||
mockUseInstalledPacks.mockReturnValue({
|
||||
installedPacks: ref(mockInstalledPacks),
|
||||
isLoading: ref(false),
|
||||
error: ref(null),
|
||||
startFetchInstalled: mockStartFetchInstalled
|
||||
} as any)
|
||||
|
||||
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),
|
||||
error: ref(null),
|
||||
startFetchInstalled: mockStartFetchInstalled
|
||||
} as any)
|
||||
|
||||
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),
|
||||
error: ref(null),
|
||||
startFetchInstalled: mockStartFetchInstalled
|
||||
} as any)
|
||||
|
||||
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),
|
||||
error: ref(null),
|
||||
startFetchInstalled: mockStartFetchInstalled
|
||||
} as any)
|
||||
|
||||
const { updateAvailableNodePacks } = useUpdateAvailableNodes()
|
||||
|
||||
expect(updateAvailableNodePacks.value).toHaveLength(0)
|
||||
})
|
||||
|
||||
it('excludes uninstalled packs', () => {
|
||||
mockIsPackInstalled.mockReturnValue(false)
|
||||
mockUseInstalledPacks.mockReturnValue({
|
||||
installedPacks: ref(mockInstalledPacks),
|
||||
isLoading: ref(false),
|
||||
error: ref(null),
|
||||
startFetchInstalled: mockStartFetchInstalled
|
||||
} as any)
|
||||
|
||||
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
|
||||
} as any)
|
||||
|
||||
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),
|
||||
error: ref(null),
|
||||
startFetchInstalled: mockStartFetchInstalled
|
||||
} as any)
|
||||
|
||||
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),
|
||||
error: ref(null),
|
||||
startFetchInstalled: mockStartFetchInstalled
|
||||
} as any)
|
||||
|
||||
useUpdateAvailableNodes()
|
||||
|
||||
expect(mockStartFetchInstalled).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('does not fetch when already loading', () => {
|
||||
mockUseInstalledPacks.mockReturnValue({
|
||||
installedPacks: ref([]),
|
||||
isLoading: ref(true),
|
||||
error: ref(null),
|
||||
startFetchInstalled: mockStartFetchInstalled
|
||||
} as any)
|
||||
|
||||
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
|
||||
} as any)
|
||||
|
||||
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
|
||||
} as any)
|
||||
|
||||
const { error } = useUpdateAvailableNodes()
|
||||
|
||||
expect(error.value).toBe(testError)
|
||||
})
|
||||
})
|
||||
|
||||
describe('reactivity', () => {
|
||||
it('updates when installed packs change', async () => {
|
||||
const installedPacksRef = ref([])
|
||||
mockUseInstalledPacks.mockReturnValue({
|
||||
installedPacks: installedPacksRef,
|
||||
isLoading: ref(false),
|
||||
error: ref(null),
|
||||
startFetchInstalled: mockStartFetchInstalled
|
||||
} as any)
|
||||
|
||||
const { updateAvailableNodePacks, hasUpdateAvailable } =
|
||||
useUpdateAvailableNodes()
|
||||
|
||||
// Initially empty
|
||||
expect(updateAvailableNodePacks.value).toEqual([])
|
||||
expect(hasUpdateAvailable.value).toBe(false)
|
||||
|
||||
// Update installed packs
|
||||
installedPacksRef.value = [mockInstalledPacks[0]] as any // pack-1: outdated
|
||||
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
|
||||
} as any)
|
||||
|
||||
const { updateAvailableNodePacks } = useUpdateAvailableNodes()
|
||||
|
||||
// Access the computed to trigger the logic
|
||||
expect(updateAvailableNodePacks.value).toBeDefined()
|
||||
|
||||
expect(mockCompareVersions).toHaveBeenCalledWith('2.0.0', '1.0.0')
|
||||
})
|
||||
|
||||
it('calls isSemVer to check nightly versions', () => {
|
||||
mockUseInstalledPacks.mockReturnValue({
|
||||
installedPacks: ref([mockInstalledPacks[2]]), // pack-3: nightly
|
||||
isLoading: ref(false),
|
||||
error: ref(null),
|
||||
startFetchInstalled: mockStartFetchInstalled
|
||||
} as any)
|
||||
|
||||
const { updateAvailableNodePacks } = useUpdateAvailableNodes()
|
||||
|
||||
// Access the computed to trigger the logic
|
||||
expect(updateAvailableNodePacks.value).toBeDefined()
|
||||
|
||||
expect(mockIsSemVer).toHaveBeenCalledWith('nightly-abc123')
|
||||
})
|
||||
|
||||
it('calls isPackInstalled for each pack', () => {
|
||||
mockUseInstalledPacks.mockReturnValue({
|
||||
installedPacks: ref(mockInstalledPacks),
|
||||
isLoading: ref(false),
|
||||
error: ref(null),
|
||||
startFetchInstalled: mockStartFetchInstalled
|
||||
} as any)
|
||||
|
||||
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')
|
||||
})
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user