Files
ComfyUI_frontend/tests-ui/tests/utils/packUtils.test.ts
Jin Yi e2de4b19fc Fix version detection for disabled packs (#5395)
* fix: normalize pack IDs to fix version detection for disabled packs

When a pack is disabled, ComfyUI-Manager returns it with a version suffix
(e.g., "ComfyUI-GGUF@1_1_4") while enabled packs don't have this suffix.
This inconsistency caused disabled packs to incorrectly show as having
updates available even when they were on the latest version.

Changes:
- Add normalizePackId utility to consistently remove version suffixes
- Apply normalization in refreshInstalledList and WebSocket updates
- Use the utility across conflict detection and node help modules
- Ensure pack version info is preserved in the object's ver field

This fixes the "Update Available" indicator incorrectly showing for
disabled packs that are already on the latest version.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

* feature: test code added

* test: packUtils test code added

* test: address PR review feedback for test
  improvements

  - Remove unnecessary .not.toThrow() assertion
  in useManagerQueue test
  - Add clarifying comments for version
  normalization test logic
  - Replace 'as any' with vi.mocked() for better
  type safety

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-09-06 23:11:12 -07:00

255 lines
8.0 KiB
TypeScript

import { describe, expect, it } from 'vitest'
import { normalizePackId, normalizePackKeys } from '@/utils/packUtils'
describe('packUtils', () => {
describe('normalizePackId', () => {
it('should return pack ID unchanged when no version suffix exists', () => {
expect(normalizePackId('ComfyUI-GGUF')).toBe('ComfyUI-GGUF')
expect(normalizePackId('ComfyUI-Manager')).toBe('ComfyUI-Manager')
expect(normalizePackId('simple-pack')).toBe('simple-pack')
})
it('should remove version suffix with underscores', () => {
expect(normalizePackId('ComfyUI-GGUF@1_1_4')).toBe('ComfyUI-GGUF')
expect(normalizePackId('ComfyUI-Manager@2_0_0')).toBe('ComfyUI-Manager')
expect(normalizePackId('pack@1_0_0_beta')).toBe('pack')
})
it('should remove version suffix with dots', () => {
expect(normalizePackId('ComfyUI-GGUF@1.1.4')).toBe('ComfyUI-GGUF')
expect(normalizePackId('pack@2.0.0')).toBe('pack')
})
it('should handle multiple @ symbols by only removing after first @', () => {
expect(normalizePackId('pack@1_0_0@extra')).toBe('pack')
expect(normalizePackId('my@pack@1_0_0')).toBe('my')
})
it('should handle empty string', () => {
expect(normalizePackId('')).toBe('')
})
it('should handle pack ID with @ but no version', () => {
expect(normalizePackId('pack@')).toBe('pack')
})
it('should handle special characters in pack name', () => {
expect(normalizePackId('my-pack_v2@1_0_0')).toBe('my-pack_v2')
expect(normalizePackId('pack.with.dots@2_0_0')).toBe('pack.with.dots')
expect(normalizePackId('UPPERCASE-Pack@1_0_0')).toBe('UPPERCASE-Pack')
})
it('should handle edge cases', () => {
// Only @ symbol
expect(normalizePackId('@')).toBe('')
expect(normalizePackId('@1_0_0')).toBe('')
// Whitespace
expect(normalizePackId(' pack @1_0_0')).toBe(' pack ')
expect(normalizePackId('pack @1_0_0')).toBe('pack ')
})
})
describe('normalizePackKeys', () => {
it('should normalize all keys with version suffixes', () => {
const input = {
'ComfyUI-GGUF': { ver: '1.1.4', enabled: true },
'ComfyUI-Manager@2_0_0': { ver: '2.0.0', enabled: false },
'another-pack@1_0_0': { ver: '1.0.0', enabled: true }
}
const expected = {
'ComfyUI-GGUF': { ver: '1.1.4', enabled: true },
'ComfyUI-Manager': { ver: '2.0.0', enabled: false },
'another-pack': { ver: '1.0.0', enabled: true }
}
expect(normalizePackKeys(input)).toEqual(expected)
})
it('should handle empty object', () => {
expect(normalizePackKeys({})).toEqual({})
})
it('should handle keys without version suffixes', () => {
const input = {
pack1: { data: 'value1' },
pack2: { data: 'value2' }
}
expect(normalizePackKeys(input)).toEqual(input)
})
it('should handle mixed keys (with and without versions)', () => {
const input = {
'normal-pack': { ver: '1.0.0' },
'versioned-pack@2_0_0': { ver: '2.0.0' },
'another-normal': { ver: '3.0.0' },
'another-versioned@4_0_0': { ver: '4.0.0' }
}
const expected = {
'normal-pack': { ver: '1.0.0' },
'versioned-pack': { ver: '2.0.0' },
'another-normal': { ver: '3.0.0' },
'another-versioned': { ver: '4.0.0' }
}
expect(normalizePackKeys(input)).toEqual(expected)
})
it('should handle duplicate keys after normalization (last one wins)', () => {
const input = {
'pack@1_0_0': { ver: '1.0.0', data: 'first' },
'pack@2_0_0': { ver: '2.0.0', data: 'second' },
pack: { ver: '3.0.0', data: 'third' }
}
const result = normalizePackKeys(input)
// The exact behavior depends on object iteration order,
// but there should only be one 'pack' key in the result
expect(Object.keys(result)).toEqual(['pack'])
expect(result.pack).toBeDefined()
expect(result.pack.ver).toBeDefined()
})
it('should preserve value references', () => {
const value1 = { ver: '1.0.0', complex: { nested: 'data' } }
const value2 = { ver: '2.0.0', complex: { nested: 'data2' } }
const input = {
'pack1@1_0_0': value1,
'pack2@2_0_0': value2
}
const result = normalizePackKeys(input)
// Values should be the same references, not cloned
expect(result.pack1).toBe(value1)
expect(result.pack2).toBe(value2)
})
it('should handle special characters in keys', () => {
const input = {
'@1_0_0': { ver: '1.0.0' },
'my-pack.v2@2_0_0': { ver: '2.0.0' },
'UPPERCASE@3_0_0': { ver: '3.0.0' }
}
const expected = {
'': { ver: '1.0.0' },
'my-pack.v2': { ver: '2.0.0' },
UPPERCASE: { ver: '3.0.0' }
}
expect(normalizePackKeys(input)).toEqual(expected)
})
it('should work with different value types', () => {
const input = {
'pack1@1_0_0': 'string value',
'pack2@2_0_0': 123,
'pack3@3_0_0': null,
'pack4@4_0_0': undefined,
'pack5@5_0_0': true,
pack6: []
}
const expected = {
pack1: 'string value',
pack2: 123,
pack3: null,
pack4: undefined,
pack5: true,
pack6: []
}
expect(normalizePackKeys(input)).toEqual(expected)
})
})
describe('Integration scenarios from JSDoc examples', () => {
it('should handle the examples from normalizePackId JSDoc', () => {
expect(normalizePackId('ComfyUI-GGUF')).toBe('ComfyUI-GGUF')
expect(normalizePackId('ComfyUI-GGUF@1_1_4')).toBe('ComfyUI-GGUF')
})
it('should handle the examples from normalizePackKeys JSDoc', () => {
const input = {
'ComfyUI-GGUF': { ver: '1.1.4', enabled: true },
'ComfyUI-Manager@2_0_0': { ver: '2.0.0', enabled: false }
}
const expected = {
'ComfyUI-GGUF': { ver: '1.1.4', enabled: true },
'ComfyUI-Manager': { ver: '2.0.0', enabled: false }
}
expect(normalizePackKeys(input)).toEqual(expected)
})
})
describe('Real-world scenarios', () => {
it('should handle typical ComfyUI-Manager response with mixed enabled/disabled packs', () => {
// Simulating actual server response pattern
const serverResponse = {
// Enabled packs come without version suffix
'ComfyUI-Essential': { ver: '1.2.3', enabled: true, aux_id: undefined },
'ComfyUI-Impact': { ver: '2.0.0', enabled: true, aux_id: undefined },
// Disabled packs come with version suffix
'ComfyUI-GGUF@1_1_4': {
ver: '1.1.4',
enabled: false,
aux_id: undefined
},
'ComfyUI-Manager@2_5_0': {
ver: '2.5.0',
enabled: false,
aux_id: undefined
}
}
const normalized = normalizePackKeys(serverResponse)
// All keys should be normalized (no version suffixes)
expect(Object.keys(normalized)).toEqual([
'ComfyUI-Essential',
'ComfyUI-Impact',
'ComfyUI-GGUF',
'ComfyUI-Manager'
])
// Values should be preserved
expect(normalized['ComfyUI-GGUF']).toEqual({
ver: '1.1.4',
enabled: false,
aux_id: undefined
})
})
it('should allow consistent access by pack ID regardless of enabled state', () => {
const packsBeforeToggle = {
'my-pack': { ver: '1.0.0', enabled: true }
}
const packsAfterToggle = {
'my-pack@1_0_0': { ver: '1.0.0', enabled: false }
}
const normalizedBefore = normalizePackKeys(packsBeforeToggle)
const normalizedAfter = normalizePackKeys(packsAfterToggle)
// Both should have the same key after normalization
expect(normalizedBefore['my-pack']).toBeDefined()
expect(normalizedAfter['my-pack']).toBeDefined()
// Can access by the same key regardless of the original format
expect(Object.keys(normalizedBefore)).toEqual(
Object.keys(normalizedAfter)
)
})
})
})