diff --git a/src/components/dialog/content/manager/ManagerDialogContent.vue b/src/components/dialog/content/manager/ManagerDialogContent.vue index 989e6d8e9..06470de7d 100644 --- a/src/components/dialog/content/manager/ManagerDialogContent.vue +++ b/src/components/dialog/content/manager/ManagerDialogContent.vue @@ -29,10 +29,10 @@
-
+

{{ $t('manager.conflicts.warningBanner.title') }}

@@ -46,6 +46,14 @@ {{ $t('manager.conflicts.warningBanner.button') }}

+
([ { id: ManagerTab.All, label: t('g.all'), icon: 'pi-list' }, @@ -510,12 +522,8 @@ watch([searchQuery, selectedTab], () => { } }) -// Automatically mark conflicts as seen when banner is displayed -// This ensures red dots disappear and banner is dismissed once user sees it watchEffect(() => { - if (shouldShowManagerBanner.value) { - markConflictsAsSeen() - } + dismissRedDotNotification() }) onBeforeUnmount(() => { diff --git a/src/components/dialog/content/manager/NodeConflictDialogContent.vue b/src/components/dialog/content/manager/NodeConflictDialogContent.vue index 07814f600..621d9d4c9 100644 --- a/src/components/dialog/content/manager/NodeConflictDialogContent.vue +++ b/src/components/dialog/content/manager/NodeConflictDialogContent.vue @@ -129,7 +129,6 @@ const props = withDefaults(defineProps(), { conflictedPackages: () => [] }) - const { t } = useI18n() const conflictsExpanded = ref(false) diff --git a/src/components/dialog/content/manager/button/PackEnableToggle.vue b/src/components/dialog/content/manager/button/PackEnableToggle.vue index d876adfa0..3ae378d04 100644 --- a/src/components/dialog/content/manager/button/PackEnableToggle.vue +++ b/src/components/dialog/content/manager/button/PackEnableToggle.vue @@ -12,18 +12,19 @@
+ @@ -52,12 +53,43 @@ const { t } = useI18n() const { isPackEnabled, enablePack, disablePack } = useComfyManagerStore() const { getConflictsForPackageByID } = useConflictDetectionStore() const { showNodeConflictDialog } = useDialogService() -const { acknowledgeConflict, isConflictAcknowledged } = - useConflictAcknowledgment() +const { acknowledgmentState, markConflictsAsSeen } = useConflictAcknowledgment() const isLoading = ref(false) const isEnabled = computed(() => isPackEnabled(nodePack.id)) +const packageConflict = computed(() => + getConflictsForPackageByID(nodePack.id || '') +) + +const canToggleDirectly = computed(() => { + return !( + hasConflict && + !acknowledgmentState.value.modal_dismissed && + packageConflict.value + ) +}) + +const showConflictModal = () => { + if (packageConflict.value && !acknowledgmentState.value.modal_dismissed) { + showNodeConflictDialog({ + conflictedPackages: [packageConflict.value], + buttonText: !isEnabled.value + ? t('manager.conflicts.enableAnyway') + : t('manager.conflicts.understood'), + onButtonClick: async () => { + if (!isEnabled.value) { + await handleEnable() + } + }, + dialogComponentProps: { + onClose: () => { + markConflictsAsSeen() + } + } + }) + } +} const handleEnable = () => { if (!nodePack.id) { @@ -90,73 +122,30 @@ const handleDisable = () => { }) } -const performToggle = async (enable: boolean) => { +const handleToggle = async (enable: boolean) => { if (isLoading.value) return + isLoading.value = true - try { - if (enable) { - await handleEnable() - } else { - await handleDisable() - } - } finally { - isLoading.value = false + if (enable) { + await handleEnable() + } else { + await handleDisable() } + isLoading.value = false } const onToggle = debounce( (enable: boolean) => { - void performToggle(enable) + void handleToggle(enable) }, TOGGLE_DEBOUNCE_MS, { trailing: true } ) -const handleToggleClick = async (enable: boolean) => { - if (isLoading.value) return - - if (enable && hasConflict) { - const conflicts = getConflictsForPackageByID(nodePack.id || '') - if (conflicts) { - const hasUnacknowledgedConflicts = conflicts.conflicts.some( - (conflict) => !isConflictAcknowledged(nodePack.id || '', conflict.type) - ) - - if (hasUnacknowledgedConflicts) { - showNodeConflictDialog({ - conflictedPackages: [conflicts], - buttonText: t('manager.conflicts.enableAnyway'), - onButtonClick: async () => { - for (const conflict of conflicts.conflicts) { - acknowledgeConflict(nodePack.id || '', conflict.type, '0.1.0') - } - await performToggle(true) - } - }) - return - } - } - } - await performToggle(enable) -} - -const showConflictModal = () => { - const conflicts = getConflictsForPackageByID(nodePack.id || '') - if (conflicts) { - showNodeConflictDialog({ - conflictedPackages: [conflicts], - buttonText: isEnabled.value - ? t('manager.conflicts.understood') - : t('manager.conflicts.enableAnyway'), - onButtonClick: async () => { - for (const conflict of conflicts.conflicts) { - acknowledgeConflict(nodePack.id || '', conflict.type, '0.1.0') - } - if (!isEnabled.value) { - onToggle(true) - } - } - }) +const handleToggleInteraction = async (event: Event) => { + if (!canToggleDirectly.value) { + event.preventDefault() + showConflictModal() } } diff --git a/src/components/dialog/content/manager/button/PackInstallButton.vue b/src/components/dialog/content/manager/button/PackInstallButton.vue index c1986c71f..6dd82c8c4 100644 --- a/src/components/dialog/content/manager/button/PackInstallButton.vue +++ b/src/components/dialog/content/manager/button/PackInstallButton.vue @@ -11,7 +11,6 @@ :loading-message="$t('g.installing')" :has-warning="hasConflict" @action="installAllPacks" - @click="onClick" /> @@ -19,26 +18,32 @@ import { inject, ref } from 'vue' import PackActionButton from '@/components/dialog/content/manager/button/PackActionButton.vue' +import { useConflictAcknowledgment } from '@/composables/useConflictAcknowledgment' +import { t } from '@/i18n' +import { useDialogService } from '@/services/dialogService' import { useComfyManagerStore } from '@/stores/comfyManagerStore' import { IsInstallingKey } from '@/types/comfyManagerTypes' import type { components } from '@/types/comfyRegistryTypes' +import { + type ConflictDetail, + type ConflictDetectionResult +} from '@/types/conflictDetectionTypes' import { components as ManagerComponents } from '@/types/generatedManagerTypes' type NodePack = components['schemas']['Node'] -const { nodePacks, variant, label, hasConflict } = defineProps<{ +const { nodePacks, variant, label, hasConflict, conflictInfo } = defineProps<{ nodePacks: NodePack[] variant?: 'default' | 'black' label?: string hasConflict?: boolean + conflictInfo?: ConflictDetail[] }>() const isInstalling = inject(IsInstallingKey, ref(false)) const managerStore = useComfyManagerStore() - -const onClick = (): void => { - isInstalling.value = true -} +const { acknowledgmentState, markConflictsAsSeen } = useConflictAcknowledgment() +const { showNodeConflictDialog } = useDialogService() const createPayload = ( installItem: NodePack @@ -69,36 +74,40 @@ const installPack = (item: NodePack) => const installAllPacks = async () => { if (!nodePacks?.length) return - // TBD Install Anyway modal - // if (hasConflict && !isConflictAcknowledged) { - // showNodeConflictDialog({ - // conflictedPackages: nodePacks, - // buttonText: t('manager.conflicts.installAnyway'), - // onButtonClick: async () => { - // // User chose "Install Anyway" - acknowledge all conflicts and proceed - // for (const conflictedPack of packsWithConflicts) { - // for (const conflict of conflictedPack.conflicts) { - // acknowledgeConflict( - // conflictedPack.package_id, - // conflict.type, - // '0.1.0' - // ) - // } - // } - // // Proceed with installation - // await performInstallation(uninstalledPacks) - // } - // }) - // return - // } - - const uninstalledPacks = nodePacks.filter( - (pack) => !managerStore.isPackInstalled(pack.id) - ) - if (!uninstalledPacks.length) return + if ( + hasConflict && + conflictInfo && + !acknowledgmentState.value.modal_dismissed + ) { + const conflictedPackages: ConflictDetectionResult[] = nodePacks.map( + (pack) => ({ + package_id: pack.id || '', + package_name: pack.name || '', + has_conflict: true, + conflicts: conflictInfo || [], + is_compatible: false + }) + ) + showNodeConflictDialog({ + conflictedPackages, + buttonText: t('manager.conflicts.installAnyway'), + onButtonClick: async () => { + // Proceed with installation + isInstalling.value = true + await performInstallation(nodePacks) + }, + dialogComponentProps: { + onClose: () => { + markConflictsAsSeen() + } + } + }) + return + } // No conflicts or conflicts acknowledged - proceed with installation - await performInstallation(uninstalledPacks) + isInstalling.value = true + await performInstallation(nodePacks) } const performInstallation = async (packs: NodePack[]) => { diff --git a/src/components/dialog/content/manager/infoPanel/InfoPanel.vue b/src/components/dialog/content/manager/infoPanel/InfoPanel.vue index a903e7bdb..c402abfdc 100644 --- a/src/components/dialog/content/manager/infoPanel/InfoPanel.vue +++ b/src/components/dialog/content/manager/infoPanel/InfoPanel.vue @@ -18,7 +18,10 @@ class="flex" style="align-items: center" > - + @@ -47,20 +51,17 @@ const formattedDownloads = computed(() => const { getConflictsForPackageByID } = useConflictDetectionStore() const { checkNodeCompatibility } = useConflictDetection() -const hasConflict = computed(() => { +const installedPackHasConflict = computed(() => { if (!nodePack.id) return false - // For installed packages, check conflicts from store - if (isInstalled.value) { - // Try exact match first - let conflicts = getConflictsForPackageByID(nodePack.id) - if (conflicts) return true + // Try exact match first + let conflicts = getConflictsForPackageByID(nodePack.id) + if (conflicts) return true - return false - } + return false +}) - // For uninstalled packages, check compatibility directly - const compatibility = checkNodeCompatibility(nodePack) - return compatibility.hasConflict +const uninstalledPackConflict = computed(() => { + return checkNodeCompatibility(nodePack) }) diff --git a/src/components/helpcenter/HelpCenterMenuContent.vue b/src/components/helpcenter/HelpCenterMenuContent.vue index 3da20af10..7333f5cf6 100644 --- a/src/components/helpcenter/HelpCenterMenuContent.vue +++ b/src/components/helpcenter/HelpCenterMenuContent.vue @@ -142,7 +142,7 @@ import { import { useI18n } from 'vue-i18n' import PuzzleIcon from '@/components/icons/PuzzleIcon.vue' -import { useConflictBannerState } from '@/composables/useConflictBannerState' +import { useConflictAcknowledgment } from '@/composables/useConflictAcknowledgment' import { useDialogService } from '@/services/dialogService' import { type ReleaseNote } from '@/services/releaseService' import { useCommandStore } from '@/stores/commandStore' @@ -215,10 +215,9 @@ const moreMenuItem = computed(() => menuItems.value.find((item) => item.key === 'more') ) -// Use conflict banner state from composable -const conflictBannerState = useConflictBannerState() -const { shouldShowConflictRedDot: shouldShowManagerRedDot } = - conflictBannerState +// Use conflict acknowledgment state from composable +const { shouldShowRedDot: shouldShowManagerRedDot } = + useConflictAcknowledgment() const menuItems = computed(() => { const moreItems: MenuItem[] = [ diff --git a/src/components/sidebar/SidebarHelpCenterIcon.vue b/src/components/sidebar/SidebarHelpCenterIcon.vue index 88f17fffc..a1c9e5645 100644 --- a/src/components/sidebar/SidebarHelpCenterIcon.vue +++ b/src/components/sidebar/SidebarHelpCenterIcon.vue @@ -63,7 +63,7 @@ import { computed, onMounted, ref } from 'vue' import HelpCenterMenuContent from '@/components/helpcenter/HelpCenterMenuContent.vue' import ReleaseNotificationToast from '@/components/helpcenter/ReleaseNotificationToast.vue' import WhatsNewPopup from '@/components/helpcenter/WhatsNewPopup.vue' -import { useConflictBannerState } from '@/composables/useConflictBannerState' +import { useConflictAcknowledgment } from '@/composables/useConflictAcknowledgment' import { useConflictDetection } from '@/composables/useConflictDetection' import { useDialogService } from '@/services/dialogService' import { useReleaseStore } from '@/stores/releaseStore' @@ -74,12 +74,13 @@ import SidebarIcon from './SidebarIcon.vue' const settingStore = useSettingStore() const releaseStore = useReleaseStore() const conflictDetection = useConflictDetection() -const conflictBannerState = useConflictBannerState() -const dialogService = useDialogService() +const conflictAcknowledgment = useConflictAcknowledgment() +const { showNodeConflictDialog } = useDialogService() const isHelpCenterVisible = ref(false) -// Use conflict banner state from composable -const { shouldShowConflictRedDot } = conflictBannerState +// Use conflict acknowledgment state from composable +const { shouldShowRedDot: shouldShowConflictRedDot, markConflictsAsSeen } = + conflictAcknowledgment // Use either release red dot or conflict red dot const shouldShowRedDot = computed(() => { @@ -123,17 +124,14 @@ const handleWhatsNewDismissed = async () => { * Show the node conflict dialog with current conflict data */ const showConflictModal = () => { - // Pass conflict data to the dialog, including onClose callback const conflictData = { conflictedPackages: conflictDetection.conflictedPackages.value } - - // Show dialog with onClose callback in dialogComponentProps - dialogService.showNodeConflictDialog({ + showNodeConflictDialog({ ...conflictData, dialogComponentProps: { onClose: () => { - conflictDetection.dismissConflictModal() + markConflictsAsSeen() } } }) diff --git a/src/composables/useConflictAcknowledgment.ts b/src/composables/useConflictAcknowledgment.ts index 75a8336b0..b352a21a9 100644 --- a/src/composables/useConflictAcknowledgment.ts +++ b/src/composables/useConflictAcknowledgment.ts @@ -1,34 +1,24 @@ import { useStorage } from '@vueuse/core' import { computed } from 'vue' +import { useConflictDetectionStore } from '@/stores/conflictDetectionStore' + /** * LocalStorage keys for conflict acknowledgment tracking */ const STORAGE_KEYS = { - CONFLICT_MODAL_DISMISSED: 'comfy_manager_conflict_banner_dismissed', - CONFLICT_RED_DOT_DISMISSED: 'comfy_help_center_conflict_seen', - ACKNOWLEDGED_CONFLICTS: 'comfy_conflict_acknowledged', - LAST_COMFYUI_VERSION: 'comfyui.last_version' + CONFLICT_MODAL_DISMISSED: 'Comfy.ConflictModalDismissed', + CONFLICT_RED_DOT_DISMISSED: 'Comfy.ConflictRedDotDismissed', + CONFLICT_WARNING_BANNER_DISMISSED: 'Comfy.ConflictWarningBannerDismissed' } as const -/** - * Interface for tracking individual conflict acknowledgments - */ -interface AcknowledgedConflict { - package_id: string - conflict_type: string - timestamp: string - comfyui_version: string -} - /** * Interface for conflict acknowledgment state */ interface ConflictAcknowledgmentState { modal_dismissed: boolean red_dot_dismissed: boolean - acknowledged_conflicts: AcknowledgedConflict[] - last_comfyui_version: string + warning_banner_dismissed: boolean } /** @@ -41,6 +31,8 @@ interface ConflictAcknowledgmentState { * - Detecting ComfyUI version changes to reset acknowledgment state */ export function useConflictAcknowledgment() { + const conflictDetectionStore = useConflictDetectionStore() + // Reactive state using VueUse's useStorage for automatic persistence const modalDismissed = useStorage( STORAGE_KEYS.CONFLICT_MODAL_DISMISSED, @@ -50,156 +42,50 @@ export function useConflictAcknowledgment() { STORAGE_KEYS.CONFLICT_RED_DOT_DISMISSED, false ) - const acknowledgedConflicts = useStorage( - STORAGE_KEYS.ACKNOWLEDGED_CONFLICTS, - [] + const warningBannerDismissed = useStorage( + STORAGE_KEYS.CONFLICT_WARNING_BANNER_DISMISSED, + false ) - const lastComfyUIVersion = useStorage(STORAGE_KEYS.LAST_COMFYUI_VERSION, '') // Create computed state object for backward compatibility const state = computed(() => ({ modal_dismissed: modalDismissed.value, red_dot_dismissed: redDotDismissed.value, - acknowledged_conflicts: acknowledgedConflicts.value, - last_comfyui_version: lastComfyUIVersion.value + warning_banner_dismissed: warningBannerDismissed.value })) - /** - * Check if ComfyUI version has changed since last run - * If version changed, reset acknowledgment state - */ - function checkComfyUIVersionChange(currentVersion: string): boolean { - const lastVersion = lastComfyUIVersion.value - const versionChanged = lastVersion !== '' && lastVersion !== currentVersion - - if (versionChanged) { - console.log( - `[ConflictAcknowledgment] ComfyUI version changed from ${lastVersion} to ${currentVersion}, resetting acknowledgment state` - ) - resetAcknowledgmentState() - } - - // Update last known version - lastComfyUIVersion.value = currentVersion - - return versionChanged - } - - /** - * Reset all acknowledgment state (called when ComfyUI version changes) - */ - function resetAcknowledgmentState(): void { - modalDismissed.value = false - redDotDismissed.value = false - acknowledgedConflicts.value = [] - } - - /** - * Mark conflict modal as dismissed - */ - function dismissConflictModal(): void { - modalDismissed.value = true - console.log('[ConflictAcknowledgment] Conflict modal dismissed') - } - /** * Mark red dot notification as dismissed */ function dismissRedDotNotification(): void { redDotDismissed.value = true - console.log('[ConflictAcknowledgment] Red dot notification dismissed') } /** - * Acknowledge a specific conflict for a package + * Mark manager warning banner as dismissed */ - function acknowledgeConflict( - packageId: string, - conflictType: string, - comfyuiVersion: string - ): void { - const acknowledgment: AcknowledgedConflict = { - package_id: packageId, - conflict_type: conflictType, - timestamp: new Date().toISOString(), - comfyui_version: comfyuiVersion - } - - // Remove any existing acknowledgment for the same package and conflict type - acknowledgedConflicts.value = acknowledgedConflicts.value.filter( - (ack) => - !(ack.package_id === packageId && ack.conflict_type === conflictType) - ) - - // Add new acknowledgment - acknowledgedConflicts.value.push(acknowledgment) - - console.log( - `[ConflictAcknowledgment] Acknowledged conflict for ${packageId}:${conflictType}` - ) + function dismissWarningBanner(): void { + warningBannerDismissed.value = true + redDotDismissed.value = true } /** - * Check if a specific conflict has been acknowledged + * Mark conflicts as seen (unified function for help center and manager) */ - function isConflictAcknowledged( - packageId: string, - conflictType: string - ): boolean { - return acknowledgedConflicts.value.some( - (ack) => - ack.package_id === packageId && ack.conflict_type === conflictType - ) + function markConflictsAsSeen(): void { + redDotDismissed.value = true + modalDismissed.value = true + warningBannerDismissed.value = true } - /** - * Remove acknowledgment for a specific conflict - */ - function removeConflictAcknowledgment( - packageId: string, - conflictType: string - ): void { - acknowledgedConflicts.value = acknowledgedConflicts.value.filter( - (ack) => - !(ack.package_id === packageId && ack.conflict_type === conflictType) - ) - console.log( - `[ConflictAcknowledgment] Removed acknowledgment for ${packageId}:${conflictType}` - ) - } - - /** - * Clear all acknowledgments (for debugging/admin purposes) - */ - function clearAllAcknowledgments(): void { - resetAcknowledgmentState() - console.log('[ConflictAcknowledgment] Cleared all acknowledgments') - } - - // Computed properties + const hasConflicts = computed(() => conflictDetectionStore.hasConflicts) const shouldShowConflictModal = computed(() => !modalDismissed.value) - const shouldShowRedDot = computed(() => !redDotDismissed.value) - - /** - * Get all acknowledged package IDs - */ - const acknowledgedPackageIds = computed(() => { - return Array.from( - new Set(acknowledgedConflicts.value.map((ack) => ack.package_id)) - ) + const shouldShowRedDot = computed(() => { + if (!hasConflicts.value) return false + return !redDotDismissed.value }) - - /** - * Get acknowledgment statistics - */ - const acknowledgmentStats = computed(() => { - return { - total_acknowledged: acknowledgedConflicts.value.length, - unique_packages: acknowledgedPackageIds.value.length, - modal_dismissed: modalDismissed.value, - red_dot_dismissed: redDotDismissed.value, - last_comfyui_version: lastComfyUIVersion.value - } + const shouldShowManagerBanner = computed(() => { + return hasConflicts.value && !warningBannerDismissed.value }) return { @@ -207,17 +93,11 @@ export function useConflictAcknowledgment() { acknowledgmentState: state, shouldShowConflictModal, shouldShowRedDot, - acknowledgedPackageIds, - acknowledgmentStats, + shouldShowManagerBanner, // Methods - checkComfyUIVersionChange, - dismissConflictModal, dismissRedDotNotification, - acknowledgeConflict, - isConflictAcknowledged, - removeConflictAcknowledgment, - clearAllAcknowledgments, - resetAcknowledgmentState + dismissWarningBanner, + markConflictsAsSeen } } diff --git a/src/composables/useConflictBannerState.ts b/src/composables/useConflictBannerState.ts deleted file mode 100644 index a89ed5747..000000000 --- a/src/composables/useConflictBannerState.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { useStorage } from '@vueuse/core' -import { computed } from 'vue' - -import { useConflictDetectionStore } from '@/stores/conflictDetectionStore' - -/** - * Composable for managing conflict banner state across components - * Provides centralized logic for conflict visibility and dismissal - */ -export function useConflictBannerState() { - const conflictDetectionStore = useConflictDetectionStore() - - // Storage keys - const HELP_CENTER_CONFLICT_SEEN_KEY = 'comfy_help_center_conflict_seen' - const MANAGER_CONFLICT_BANNER_DISMISSED_KEY = - 'comfy_manager_conflict_banner_dismissed' - - // Reactive storage state - const hasSeenConflicts = useStorage(HELP_CENTER_CONFLICT_SEEN_KEY, false) - const isConflictBannerDismissed = useStorage( - MANAGER_CONFLICT_BANNER_DISMISSED_KEY, - false - ) - - // Computed states - const hasConflicts = computed(() => conflictDetectionStore.hasConflicts) - - /** - * Check if the help center should show a red dot for conflicts - */ - const shouldShowConflictRedDot = computed(() => { - if (!hasConflicts.value) return false - return !hasSeenConflicts.value - }) - - /** - * Check if the manager conflict banner should be visible - */ - const shouldShowManagerBanner = computed(() => { - return hasConflicts.value && !isConflictBannerDismissed.value - }) - - /** - * Mark conflicts as seen (used when user opens manager dialog or help center) - */ - const markConflictsAsSeen = () => { - if (hasConflicts.value) { - hasSeenConflicts.value = true - isConflictBannerDismissed.value = true - - // Force localStorage update as backup due to useStorage sync timing issue - // useStorage updates localStorage asynchronously, but we need immediate persistence - localStorage.setItem(HELP_CENTER_CONFLICT_SEEN_KEY, 'true') - localStorage.setItem(MANAGER_CONFLICT_BANNER_DISMISSED_KEY, 'true') - } - } - - return { - // State - hasConflicts, - hasSeenConflicts, - isConflictBannerDismissed, - - // Computed - shouldShowConflictRedDot, - shouldShowManagerBanner, - - // Actions - markConflictsAsSeen - } -} diff --git a/src/composables/useConflictDetection.ts b/src/composables/useConflictDetection.ts index 60bc0890a..fb5b67750 100644 --- a/src/composables/useConflictDetection.ts +++ b/src/composables/useConflictDetection.ts @@ -261,8 +261,8 @@ export function useConflictDetection() { // Combine local installation data with version-specific Registry data const requirement: NodePackRequirements = { // Basic package info - package_id: packageId, - package_name: pack.name || packageId, + id: pack.id, + name: pack.name, installed_version: installedVersion, is_enabled: isEnabled, @@ -287,8 +287,8 @@ export function useConflictDetection() { // Create fallback requirement without Registry data const fallbackRequirement: NodePackRequirements = { - package_id: packageId, - package_name: pack.name || packageId, + id: pack.id, + name: pack.name, installed_version: installedVersion, is_enabled: isEnabled, is_banned: false, @@ -381,8 +381,8 @@ export function useConflictDetection() { const hasConflict = conflicts.length > 0 return { - package_id: packageReq.package_id, - package_name: packageReq.package_name, + package_id: packageReq.id ?? '', + package_name: packageReq.name ?? '', has_conflict: hasConflict, conflicts, is_compatible: !hasConflict @@ -528,7 +528,7 @@ export function useConflictDetection() { return detectPackageConflicts(packageReq, sysEnv) } catch (error) { console.warn( - `[ConflictDetection] Failed to detect conflicts for package ${packageReq.package_name}:`, + `[ConflictDetection] Failed to detect conflicts for package ${packageReq.name}:`, error ) // Return null for failed packages, will be filtered out @@ -589,14 +589,6 @@ export function useConflictDetection() { ) storedMergedConflicts.value = [...mergedConflicts] - // Check for ComfyUI version change to reset acknowledgments - if (sysEnv.comfyui_version !== 'unknown') { - acknowledgment.checkComfyUIVersionChange(sysEnv.comfyui_version) - } - - // TODO: Show red dot on Help Center based on acknowledgment.shouldShowRedDot - // TODO: Store conflict state for event-based dialog triggers - // Use merged conflicts in response as well const response: ConflictDetectionResponse = { success: true, @@ -708,31 +700,6 @@ export function useConflictDetection() { return hasActualConflicts && canShowModal } - /** - * Mark conflict modal as dismissed - */ - function dismissConflictModal(): void { - acknowledgment.dismissConflictModal() - } - - /** - * Mark red dot notification as dismissed - */ - function dismissRedDotNotification(): void { - acknowledgment.dismissRedDotNotification() - } - - /** - * Acknowledge a specific conflict - */ - function acknowledgePackageConflict( - packageId: string, - conflictType: string - ): void { - const currentVersion = systemEnvironment.value?.comfyui_version || 'unknown' - acknowledgment.acknowledgeConflict(packageId, conflictType, currentVersion) - } - /** * Check compatibility for a node. * Used by components like PackVersionSelectorPopover. @@ -844,20 +811,12 @@ export function useConflictDetection() { bannedPackages, securityPendingPackages, - // Acknowledgment state - shouldShowConflictModal: acknowledgment.shouldShowConflictModal, - shouldShowRedDot: acknowledgment.shouldShowRedDot, - acknowledgedPackageIds: acknowledgment.acknowledgedPackageIds, - // Methods performConflictDetection, detectSystemEnvironment, initializeConflictDetection, cancelRequests, shouldShowConflictModalAfterUpdate, - dismissConflictModal, - dismissRedDotNotification, - acknowledgePackageConflict, // Helper functions for other components checkNodeCompatibility diff --git a/src/types/conflictDetectionTypes.ts b/src/types/conflictDetectionTypes.ts index d6511ca7e..c513782ab 100644 --- a/src/types/conflictDetectionTypes.ts +++ b/src/types/conflictDetectionTypes.ts @@ -53,8 +53,6 @@ export interface NodePackRequirements extends Node { is_banned: boolean is_pending: boolean // Aliases for backwards compatibility with existing code - package_id: string - package_name: string version_status?: string } diff --git a/tests-ui/tests/composables/useConflictAcknowledgment.spec.ts b/tests-ui/tests/composables/useConflictAcknowledgment.spec.ts index 908635703..8df790243 100644 --- a/tests-ui/tests/composables/useConflictAcknowledgment.spec.ts +++ b/tests-ui/tests/composables/useConflictAcknowledgment.spec.ts @@ -1,9 +1,12 @@ +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 @@ -18,20 +21,20 @@ describe('useConflictAcknowledgment with useStorage refactor', () => { const { shouldShowConflictModal, shouldShowRedDot, - acknowledgedPackageIds + shouldShowManagerBanner } = useConflictAcknowledgment() expect(shouldShowConflictModal.value).toBe(true) - expect(shouldShowRedDot.value).toBe(true) - expect(acknowledgedPackageIds.value).toEqual([]) + expect(shouldShowRedDot.value).toBe(false) // No conflicts initially + expect(shouldShowManagerBanner.value).toBe(false) // No conflicts initially }) it('should dismiss modal state correctly', () => { - const { dismissConflictModal, shouldShowConflictModal } = + const { markConflictsAsSeen, shouldShowConflictModal } = useConflictAcknowledgment() expect(shouldShowConflictModal.value).toBe(true) - dismissConflictModal() + markConflictsAsSeen() expect(shouldShowConflictModal.value).toBe(false) }) @@ -39,92 +42,78 @@ describe('useConflictAcknowledgment with useStorage refactor', () => { const { dismissRedDotNotification, shouldShowRedDot } = useConflictAcknowledgment() - expect(shouldShowRedDot.value).toBe(true) + expect(shouldShowRedDot.value).toBe(false) // No conflicts initially dismissRedDotNotification() expect(shouldShowRedDot.value).toBe(false) }) - it('should acknowledge conflicts correctly', () => { - const { - acknowledgeConflict, - isConflictAcknowledged, - acknowledgedPackageIds - } = useConflictAcknowledgment() - - expect(acknowledgedPackageIds.value).toEqual([]) - - acknowledgeConflict('package1', 'version_conflict', '1.0.0') - - expect(isConflictAcknowledged('package1', 'version_conflict')).toBe(true) - expect(isConflictAcknowledged('package1', 'other_conflict')).toBe(false) - expect(acknowledgedPackageIds.value).toContain('package1') - }) - - it('should reset state when ComfyUI version changes', () => { - const { - dismissConflictModal, - acknowledgeConflict, - checkComfyUIVersionChange, - shouldShowConflictModal, - acknowledgedPackageIds - } = useConflictAcknowledgment() - - // Set up some state - dismissConflictModal() - acknowledgeConflict('package1', 'conflict1', '1.0.0') - - expect(shouldShowConflictModal.value).toBe(false) - expect(acknowledgedPackageIds.value).toContain('package1') - - // First check sets the initial version, no change yet - const changed1 = checkComfyUIVersionChange('1.0.0') - expect(changed1).toBe(false) - - // Now check with different version should reset - const changed2 = checkComfyUIVersionChange('2.0.0') - expect(changed2).toBe(true) - expect(shouldShowConflictModal.value).toBe(true) - expect(acknowledgedPackageIds.value).toEqual([]) - }) - - it('should track acknowledgment statistics correctly', () => { - const { acknowledgmentStats, dismissConflictModal, acknowledgeConflict } = + it('should dismiss warning banner correctly', () => { + const { dismissWarningBanner, shouldShowManagerBanner } = useConflictAcknowledgment() - // Initial stats - expect(acknowledgmentStats.value).toEqual({ - total_acknowledged: 0, - unique_packages: 0, - modal_dismissed: false, - red_dot_dismissed: false, - last_comfyui_version: '' - }) + // Initially should not show banner (no conflicts) + expect(shouldShowManagerBanner.value).toBe(false) - // Update state - dismissConflictModal() - acknowledgeConflict('package1', 'conflict1', '1.0.0') - acknowledgeConflict('package2', 'conflict2', '1.0.0') + // Test dismissWarningBanner function exists and works + dismissWarningBanner() + expect(shouldShowManagerBanner.value).toBe(false) + }) - // Check updated stats - expect(acknowledgmentStats.value.total_acknowledged).toBe(2) - expect(acknowledgmentStats.value.unique_packages).toBe(2) - expect(acknowledgmentStats.value.modal_dismissed).toBe(true) + 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 { dismissConflictModal, acknowledgeConflict } = + const { markConflictsAsSeen, dismissWarningBanner } = useConflictAcknowledgment() - dismissConflictModal() - acknowledgeConflict('test-pkg', 'test-conflict', '1.0.0') + 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_manager_conflict_banner_dismissed') + localStorage.getItem('Comfy.ConflictWarningBannerDismissed') ).not.toBeNull() - expect(localStorage.getItem('comfy_conflict_acknowledged')).not.toBeNull() }) }) diff --git a/tests-ui/tests/composables/useConflictAcknowledgment.test.ts b/tests-ui/tests/composables/useConflictAcknowledgment.test.ts index 62e28589b..879bde1e4 100644 --- a/tests-ui/tests/composables/useConflictAcknowledgment.test.ts +++ b/tests-ui/tests/composables/useConflictAcknowledgment.test.ts @@ -1,426 +1,138 @@ +import { createPinia, setActivePinia } from 'pinia' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { useConflictAcknowledgment } from '@/composables/useConflictAcknowledgment' describe('useConflictAcknowledgment', () => { - // Mock localStorage - const mockLocalStorage = { - getItem: vi.fn(), - setItem: vi.fn(), - removeItem: vi.fn(), - clear: vi.fn() - } - beforeEach(() => { - // Reset localStorage mock - mockLocalStorage.getItem.mockClear() - mockLocalStorage.setItem.mockClear() - mockLocalStorage.removeItem.mockClear() - mockLocalStorage.clear.mockClear() - - // Mock localStorage globally - Object.defineProperty(global, 'localStorage', { - value: mockLocalStorage, - writable: true - }) - - // Default mock returns - mockLocalStorage.getItem.mockReturnValue(null) + // Set up Pinia for each test + setActivePinia(createPinia()) + // Clear localStorage before each test + localStorage.clear() + // Reset modules to ensure fresh state + vi.resetModules() }) afterEach(() => { - vi.restoreAllMocks() + localStorage.clear() }) describe('initial state loading', () => { it('should load empty state when localStorage is empty', () => { - mockLocalStorage.getItem.mockReturnValue(null) - const { acknowledgmentState } = useConflictAcknowledgment() expect(acknowledgmentState.value).toEqual({ modal_dismissed: false, red_dot_dismissed: false, - acknowledged_conflicts: [], - last_comfyui_version: '' + warning_banner_dismissed: false }) }) it('should load existing state from localStorage', () => { - mockLocalStorage.getItem.mockImplementation((key) => { - switch (key) { - case 'comfy_manager_conflict_banner_dismissed': - return 'true' - case 'comfy_help_center_conflict_seen': - return 'true' - case 'comfy_conflict_acknowledged': - return JSON.stringify([ - { - package_id: 'TestPackage', - conflict_type: 'os', - timestamp: '2023-01-01T00:00:00.000Z', - comfyui_version: '0.3.41' - } - ]) - case 'comfyui.last_version': - return '0.3.41' - default: - return null - } - }) + // 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, - acknowledged_conflicts: [ - { - package_id: 'TestPackage', - conflict_type: 'os', - timestamp: '2023-01-01T00:00:00.000Z', - comfyui_version: '0.3.41' - } - ], - last_comfyui_version: '0.3.41' - }) - }) - - it.skip('should handle corrupted localStorage data gracefully', () => { - mockLocalStorage.getItem.mockImplementation((key) => { - if (key === 'comfy_conflict_acknowledged') { - return 'invalid-json' - } - return null - }) - - // VueUse's useStorage should handle corrupted data gracefully - const { acknowledgmentState } = useConflictAcknowledgment() - - // Should fall back to default values when localStorage contains invalid JSON - expect(acknowledgmentState.value).toEqual({ - modal_dismissed: false, - red_dot_dismissed: false, - acknowledged_conflicts: [], - last_comfyui_version: '' + warning_banner_dismissed: true }) }) }) - describe('ComfyUI version change detection', () => { - it('should detect version change and reset state', () => { - // Setup existing state - mockLocalStorage.getItem.mockImplementation((key) => { - switch (key) { - case 'comfyui.conflict.modal.dismissed': - return 'true' - case 'comfyui.conflict.red_dot.dismissed': - return 'true' - case 'comfyui.last_version': - return '0.3.40' - default: - return null - } - }) - - const consoleLogSpy = vi - .spyOn(console, 'log') - .mockImplementation(() => {}) - - const { checkComfyUIVersionChange, acknowledgmentState } = + describe('dismissal functions', () => { + it('should mark conflicts as seen with unified function', () => { + const { markConflictsAsSeen, acknowledgmentState } = useConflictAcknowledgment() - const versionChanged = checkComfyUIVersionChange('0.3.41') - - expect(versionChanged).toBe(true) - expect(acknowledgmentState.value.modal_dismissed).toBe(false) - expect(acknowledgmentState.value.red_dot_dismissed).toBe(false) - expect(acknowledgmentState.value.last_comfyui_version).toBe('0.3.41') - - expect(consoleLogSpy).toHaveBeenCalledWith( - expect.stringContaining('ComfyUI version changed from 0.3.40 to 0.3.41') - ) - - consoleLogSpy.mockRestore() - }) - - it('should not detect version change for same version', () => { - mockLocalStorage.getItem.mockImplementation((key) => { - if (key === 'comfyui.last_version') { - return '0.3.41' - } - return null - }) - - const { checkComfyUIVersionChange } = useConflictAcknowledgment() - - const versionChanged = checkComfyUIVersionChange('0.3.41') - - expect(versionChanged).toBe(false) - }) - - it('should handle first run (no previous version)', () => { - mockLocalStorage.getItem.mockReturnValue(null) - - const { checkComfyUIVersionChange } = useConflictAcknowledgment() - - const versionChanged = checkComfyUIVersionChange('0.3.41') - - expect(versionChanged).toBe(false) - }) - }) - - describe('modal dismissal', () => { - it('should dismiss conflict modal and save to localStorage', () => { - const consoleLogSpy = vi - .spyOn(console, 'log') - .mockImplementation(() => {}) - - const { dismissConflictModal, acknowledgmentState } = - useConflictAcknowledgment() - - dismissConflictModal() + markConflictsAsSeen() expect(acknowledgmentState.value.modal_dismissed).toBe(true) - // useStorage handles localStorage synchronization internally - expect(consoleLogSpy).toHaveBeenCalledWith( - '[ConflictAcknowledgment] Conflict modal dismissed' - ) - - consoleLogSpy.mockRestore() }) - it('should dismiss red dot notification and save to localStorage', () => { - const consoleLogSpy = vi - .spyOn(console, 'log') - .mockImplementation(() => {}) - + it('should dismiss red dot notification', () => { const { dismissRedDotNotification, acknowledgmentState } = useConflictAcknowledgment() dismissRedDotNotification() expect(acknowledgmentState.value.red_dot_dismissed).toBe(true) - // useStorage handles localStorage synchronization internally - expect(consoleLogSpy).toHaveBeenCalledWith( - '[ConflictAcknowledgment] Red dot notification dismissed' - ) - - consoleLogSpy.mockRestore() }) - }) - describe('conflict acknowledgment', () => { - it('should acknowledge a conflict and save to localStorage', () => { - const consoleLogSpy = vi - .spyOn(console, 'log') - .mockImplementation(() => {}) - const dateSpy = vi - .spyOn(Date.prototype, 'toISOString') - .mockReturnValue('2023-01-01T00:00:00.000Z') - - const { acknowledgeConflict, acknowledgmentState } = + it('should dismiss warning banner', () => { + const { dismissWarningBanner, acknowledgmentState } = useConflictAcknowledgment() - acknowledgeConflict('TestPackage', 'os', '0.3.41') + dismissWarningBanner() - expect(acknowledgmentState.value.acknowledged_conflicts).toHaveLength(1) - expect(acknowledgmentState.value.acknowledged_conflicts[0]).toEqual({ - package_id: 'TestPackage', - conflict_type: 'os', - timestamp: '2023-01-01T00:00:00.000Z', - comfyui_version: '0.3.41' - }) - - // useStorage handles localStorage synchronization internally - expect(acknowledgmentState.value.acknowledged_conflicts).toHaveLength(1) - expect(acknowledgmentState.value.acknowledged_conflicts[0]).toEqual({ - package_id: 'TestPackage', - conflict_type: 'os', - timestamp: '2023-01-01T00:00:00.000Z', - comfyui_version: '0.3.41' - }) - - expect(consoleLogSpy).toHaveBeenCalledWith( - '[ConflictAcknowledgment] Acknowledged conflict for TestPackage:os' - ) - - dateSpy.mockRestore() - consoleLogSpy.mockRestore() + expect(acknowledgmentState.value.warning_banner_dismissed).toBe(true) }) - it('should replace existing acknowledgment for same package and conflict type', () => { - const { acknowledgeConflict, acknowledgmentState } = + it('should mark all conflicts as seen', () => { + const { markConflictsAsSeen, acknowledgmentState } = useConflictAcknowledgment() - // First acknowledgment - acknowledgeConflict('TestPackage', 'os', '0.3.41') - expect(acknowledgmentState.value.acknowledged_conflicts).toHaveLength(1) + markConflictsAsSeen() - // Second acknowledgment for same package and conflict type - acknowledgeConflict('TestPackage', 'os', '0.3.42') - expect(acknowledgmentState.value.acknowledged_conflicts).toHaveLength(1) - expect( - acknowledgmentState.value.acknowledged_conflicts[0].comfyui_version - ).toBe('0.3.42') - }) - - it('should allow multiple acknowledgments for different conflict types', () => { - const { acknowledgeConflict, acknowledgmentState } = - useConflictAcknowledgment() - - acknowledgeConflict('TestPackage', 'os', '0.3.41') - acknowledgeConflict('TestPackage', 'accelerator', '0.3.41') - - expect(acknowledgmentState.value.acknowledged_conflicts).toHaveLength(2) - }) - }) - - describe('conflict checking', () => { - it('should check if conflict is acknowledged', () => { - const { acknowledgeConflict, isConflictAcknowledged } = - useConflictAcknowledgment() - - // Initially not acknowledged - expect(isConflictAcknowledged('TestPackage', 'os')).toBe(false) - - // After acknowledgment - acknowledgeConflict('TestPackage', 'os', '0.3.41') - expect(isConflictAcknowledged('TestPackage', 'os')).toBe(true) - - // Different conflict type should not be acknowledged - expect(isConflictAcknowledged('TestPackage', 'accelerator')).toBe(false) - }) - - it('should remove conflict acknowledgment', () => { - const consoleLogSpy = vi - .spyOn(console, 'log') - .mockImplementation(() => {}) - - const { - acknowledgeConflict, - removeConflictAcknowledgment, - isConflictAcknowledged, - acknowledgmentState - } = useConflictAcknowledgment() - - // Add acknowledgment - acknowledgeConflict('TestPackage', 'os', '0.3.41') - expect(isConflictAcknowledged('TestPackage', 'os')).toBe(true) - - // Remove acknowledgment - removeConflictAcknowledgment('TestPackage', 'os') - expect(isConflictAcknowledged('TestPackage', 'os')).toBe(false) - expect(acknowledgmentState.value.acknowledged_conflicts).toHaveLength(0) - - expect(consoleLogSpy).toHaveBeenCalledWith( - '[ConflictAcknowledgment] Removed acknowledgment for TestPackage:os' - ) - - consoleLogSpy.mockRestore() + 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, dismissConflictModal } = + const { shouldShowConflictModal, markConflictsAsSeen } = useConflictAcknowledgment() expect(shouldShowConflictModal.value).toBe(true) - dismissConflictModal() + markConflictsAsSeen() expect(shouldShowConflictModal.value).toBe(false) }) - it('should calculate shouldShowRedDot correctly', () => { + it('should calculate shouldShowRedDot correctly based on conflicts', () => { const { shouldShowRedDot, dismissRedDotNotification } = useConflictAcknowledgment() - expect(shouldShowRedDot.value).toBe(true) + // Initially false because no conflicts exist + expect(shouldShowRedDot.value).toBe(false) dismissRedDotNotification() expect(shouldShowRedDot.value).toBe(false) }) - it('should calculate acknowledgedPackageIds correctly', () => { - const { acknowledgeConflict, acknowledgedPackageIds } = + it('should calculate shouldShowManagerBanner correctly', () => { + const { shouldShowManagerBanner, dismissWarningBanner } = useConflictAcknowledgment() - expect(acknowledgedPackageIds.value).toEqual([]) + // Initially false because no conflicts exist + expect(shouldShowManagerBanner.value).toBe(false) - acknowledgeConflict('Package1', 'os', '0.3.41') - acknowledgeConflict('Package2', 'accelerator', '0.3.41') - acknowledgeConflict('Package1', 'accelerator', '0.3.41') // Same package, different conflict - - expect(acknowledgedPackageIds.value).toEqual(['Package1', 'Package2']) - }) - - it('should calculate acknowledgmentStats correctly', () => { - const { acknowledgeConflict, dismissConflictModal, acknowledgmentStats } = - useConflictAcknowledgment() - - acknowledgeConflict('Package1', 'os', '0.3.41') - acknowledgeConflict('Package2', 'accelerator', '0.3.41') - dismissConflictModal() - - expect(acknowledgmentStats.value).toEqual({ - total_acknowledged: 2, - unique_packages: 2, - modal_dismissed: true, - red_dot_dismissed: false, - last_comfyui_version: '' - }) + dismissWarningBanner() + expect(shouldShowManagerBanner.value).toBe(false) }) }) - describe('clear functionality', () => { - it('should clear all acknowledgments', () => { - const consoleLogSpy = vi - .spyOn(console, 'log') - .mockImplementation(() => {}) + describe('localStorage persistence', () => { + it('should persist to localStorage automatically', () => { + const { markConflictsAsSeen, dismissWarningBanner } = + useConflictAcknowledgment() - const { - acknowledgeConflict, - dismissConflictModal, - clearAllAcknowledgments, - acknowledgmentState - } = useConflictAcknowledgment() + markConflictsAsSeen() + dismissWarningBanner() - // Add some data - acknowledgeConflict('Package1', 'os', '0.3.41') - dismissConflictModal() - - // Clear all - clearAllAcknowledgments() - - expect(acknowledgmentState.value).toEqual({ - modal_dismissed: false, - red_dot_dismissed: false, - acknowledged_conflicts: [], - last_comfyui_version: '' - }) - - expect(consoleLogSpy).toHaveBeenCalledWith( - '[ConflictAcknowledgment] Cleared all acknowledgments' - ) - - consoleLogSpy.mockRestore() - }) - }) - - describe.skip('localStorage error handling', () => { - it('should handle localStorage setItem errors gracefully', () => { - mockLocalStorage.setItem.mockImplementation(() => { - throw new Error('localStorage full') - }) - - const { dismissConflictModal, acknowledgmentState } = useConflictAcknowledgment() - - // VueUse's useStorage should handle localStorage errors gracefully - expect(() => dismissConflictModal()).not.toThrow() - - // State should still be updated in memory even if localStorage fails - expect(acknowledgmentState.value.modal_dismissed).toBe(true) + // VueUse useStorage should automatically persist to localStorage + expect( + localStorage.getItem('Comfy.ConflictModalDismissed') + ).not.toBeNull() + expect( + localStorage.getItem('Comfy.ConflictWarningBannerDismissed') + ).not.toBeNull() }) }) }) diff --git a/tests-ui/tests/composables/useConflictDetection.test.ts b/tests-ui/tests/composables/useConflictDetection.test.ts index 1da5b2835..c7b6b81c5 100644 --- a/tests-ui/tests/composables/useConflictDetection.test.ts +++ b/tests-ui/tests/composables/useConflictDetection.test.ts @@ -906,23 +906,11 @@ describe.skip('useConflictDetection with Registry Store', () => { ) }) - it('should expose acknowledgment state and methods', () => { + it('should expose conflict modal display method', () => { const { - shouldShowConflictModal, - shouldShowRedDot, - acknowledgedPackageIds, - dismissConflictModal, - dismissRedDotNotification, - acknowledgePackageConflict, shouldShowConflictModalAfterUpdate } = useConflictDetection() - expect(shouldShowConflictModal).toBeDefined() - expect(shouldShowRedDot).toBeDefined() - expect(acknowledgedPackageIds).toBeDefined() - expect(dismissConflictModal).toBeDefined() - expect(dismissRedDotNotification).toBeDefined() - expect(acknowledgePackageConflict).toBeDefined() expect(shouldShowConflictModalAfterUpdate).toBeDefined() }) @@ -981,18 +969,8 @@ describe.skip('useConflictDetection with Registry Store', () => { expect(result).toBe(true) // Should show modal when conflicts exist and not dismissed }) - it('should call acknowledgment methods when dismissing', () => { - const { dismissConflictModal, dismissRedDotNotification } = - useConflictDetection() - dismissConflictModal() - expect(mockAcknowledgment.dismissConflictModal).toHaveBeenCalled() - - dismissRedDotNotification() - expect(mockAcknowledgment.dismissRedDotNotification).toHaveBeenCalled() - }) - - it('should acknowledge package conflicts with system version', async () => { + it('should detect system environment correctly', async () => { // Mock system environment mockSystemStatsStore.systemStats = { system: { @@ -1002,20 +980,12 @@ describe.skip('useConflictDetection with Registry Store', () => { devices: [] } - const { acknowledgePackageConflict, detectSystemEnvironment } = - useConflictDetection() + const { detectSystemEnvironment } = useConflictDetection() - // First detect system environment - await detectSystemEnvironment() + // Detect system environment + const environment = await detectSystemEnvironment() - // Then acknowledge conflict - acknowledgePackageConflict('TestPackage', 'os') - - expect(mockAcknowledgment.acknowledgeConflict).toHaveBeenCalledWith( - 'TestPackage', - 'os', - '0.3.41' // System version from mock data - ) + expect(environment.comfyui_version).toBe('0.3.41') }) })