= {
- 'g.close': 'Close',
- 'whatsNewPopup.noReleaseNotes': 'No release notes available'
- }
- return translations[key] || key
- })
- }
- }
- })
- }
-
- beforeEach(async () => {
- vi.clearAllMocks()
-
- // Reset mock store
- mockReleaseStore.recentRelease = null
- mockReleaseStore.shouldShowPopup = false
- mockReleaseStore.releases = []
-
- // Mock release store
- const { useReleaseStore } = await import('@/stores/releaseStore')
- vi.mocked(useReleaseStore).mockReturnValue(mockReleaseStore as any)
- })
-
- afterEach(() => {
- vi.restoreAllMocks()
- })
-
- describe('visibility', () => {
- it('should not show when shouldShowPopup is false', () => {
- mockReleaseStore.shouldShowPopup = false
-
- const wrapper = createWrapper()
-
- expect(wrapper.find('.whats-new-popup-container').exists()).toBe(false)
- })
-
- it('should show when shouldShowPopup is true and not dismissed', () => {
- mockReleaseStore.shouldShowPopup = true
- mockReleaseStore.recentRelease = {
- id: 1,
- project: 'comfyui_frontend',
- version: '1.24.0',
- attention: 'medium',
- content: 'New features added',
- published_at: '2023-01-01T00:00:00Z'
- }
-
- const wrapper = createWrapper()
-
- expect(wrapper.find('.whats-new-popup-container').exists()).toBe(true)
- expect(wrapper.find('.whats-new-popup').exists()).toBe(true)
- })
-
- it('should hide when dismissed locally', async () => {
- mockReleaseStore.shouldShowPopup = true
- mockReleaseStore.recentRelease = {
- id: 1,
- project: 'comfyui_frontend',
- version: '1.24.0',
- attention: 'medium',
- content: 'New features added',
- published_at: '2023-01-01T00:00:00Z'
- }
-
- const wrapper = createWrapper()
-
- // Initially visible
- expect(wrapper.find('.whats-new-popup-container').exists()).toBe(true)
-
- // Click close button
- await wrapper.find('.close-button').trigger('click')
-
- // Should be hidden
- expect(wrapper.find('.whats-new-popup-container').exists()).toBe(false)
- })
- })
-
- describe('content rendering', () => {
- it('should render release content using marked', async () => {
- mockReleaseStore.shouldShowPopup = true
- mockReleaseStore.recentRelease = {
- id: 1,
- project: 'comfyui_frontend',
- version: '1.24.0',
- attention: 'medium',
- content: '# Release Notes\n\nNew features',
- published_at: '2023-01-01T00:00:00Z'
- }
-
- const wrapper = createWrapper()
-
- // Check that the content is rendered (marked is mocked to return processed content)
- expect(wrapper.find('.content-text').exists()).toBe(true)
- const contentHtml = wrapper.find('.content-text').html()
- expect(contentHtml).toContain('# Release Notes')
- })
-
- it('should handle missing release content', () => {
- mockReleaseStore.shouldShowPopup = true
- mockReleaseStore.recentRelease = {
- id: 1,
- project: 'comfyui_frontend',
- version: '1.24.0',
- attention: 'medium',
- content: '',
- published_at: '2023-01-01T00:00:00Z'
- }
-
- const wrapper = createWrapper()
-
- expect(wrapper.find('.content-text').html()).toContain(
- 'whatsNewPopup.noReleaseNotes'
- )
- })
-
- it('should handle markdown parsing errors gracefully', () => {
- mockReleaseStore.shouldShowPopup = true
- mockReleaseStore.recentRelease = {
- id: 1,
- project: 'comfyui_frontend',
- version: '1.24.0',
- attention: 'medium',
- content: 'Content with\nnewlines',
- published_at: '2023-01-01T00:00:00Z'
- }
-
- const wrapper = createWrapper()
-
- // Should show content even without markdown processing
- expect(wrapper.find('.content-text').exists()).toBe(true)
- })
- })
-
- describe('changelog URL generation', () => {
- it('should generate English changelog URL with version anchor', () => {
- mockReleaseStore.shouldShowPopup = true
- mockReleaseStore.recentRelease = {
- id: 1,
- project: 'comfyui_frontend',
- version: '1.24.0-beta.1',
- attention: 'medium',
- content: 'Release content',
- published_at: '2023-01-01T00:00:00Z'
- }
-
- const wrapper = createWrapper()
- const learnMoreLink = wrapper.find('.learn-more-link')
-
- // formatVersionAnchor replaces dots with dashes: 1.24.0-beta.1 -> v1-24-0-beta-1
- expect(learnMoreLink.attributes('href')).toBe(
- 'https://docs.comfy.org/changelog#v1-24-0-beta-1'
- )
- })
-
- it('should generate Chinese changelog URL when locale is zh', () => {
- mockReleaseStore.shouldShowPopup = true
- mockReleaseStore.recentRelease = {
- id: 1,
- project: 'comfyui_frontend',
- version: '1.24.0',
- attention: 'medium',
- content: 'Release content',
- published_at: '2023-01-01T00:00:00Z'
- }
-
- const wrapper = createWrapper({
- global: {
- mocks: {
- $t: vi.fn((key: string) => {
- const translations: Record = {
- 'g.close': 'Close',
- 'whatsNewPopup.noReleaseNotes': 'No release notes available',
- 'whatsNewPopup.learnMore': 'Learn More'
- }
- return translations[key] || key
- })
- },
- provide: {
- // Mock vue-i18n locale as Chinese
- locale: { value: 'zh' }
- }
- }
- })
-
- // Since the locale mocking doesn't work well in tests, just check the English URL for now
- // In a real component test with proper i18n setup, this would show the Chinese URL
- const learnMoreLink = wrapper.find('.learn-more-link')
- expect(learnMoreLink.attributes('href')).toBe(
- 'https://docs.comfy.org/changelog#v1-24-0'
- )
- })
-
- it('should generate base changelog URL when no version available', () => {
- mockReleaseStore.shouldShowPopup = true
- mockReleaseStore.recentRelease = {
- id: 1,
- project: 'comfyui_frontend',
- version: '',
- attention: 'medium',
- content: 'Release content',
- published_at: '2023-01-01T00:00:00Z'
- }
-
- const wrapper = createWrapper()
- const learnMoreLink = wrapper.find('.learn-more-link')
-
- expect(learnMoreLink.attributes('href')).toBe(
- 'https://docs.comfy.org/changelog'
- )
- })
- })
-
- describe('popup dismissal', () => {
- it('should call handleWhatsNewSeen and emit event when closed', async () => {
- mockReleaseStore.shouldShowPopup = true
- mockReleaseStore.recentRelease = {
- id: 1,
- project: 'comfyui_frontend',
- version: '1.24.0',
- attention: 'medium',
- content: 'Release content',
- published_at: '2023-01-01T00:00:00Z'
- }
- mockReleaseStore.handleWhatsNewSeen.mockResolvedValue(undefined)
-
- const wrapper = createWrapper()
-
- // Click close button
- await wrapper.find('.close-button').trigger('click')
-
- expect(mockReleaseStore.handleWhatsNewSeen).toHaveBeenCalledWith('1.24.0')
- expect(wrapper.emitted('whats-new-dismissed')).toBeTruthy()
- expect(wrapper.emitted('whats-new-dismissed')).toHaveLength(1)
- })
-
- it('should close when learn more link is clicked', async () => {
- mockReleaseStore.shouldShowPopup = true
- mockReleaseStore.recentRelease = {
- id: 1,
- project: 'comfyui_frontend',
- version: '1.24.0',
- attention: 'medium',
- content: 'Release content',
- published_at: '2023-01-01T00:00:00Z'
- }
- mockReleaseStore.handleWhatsNewSeen.mockResolvedValue(undefined)
-
- const wrapper = createWrapper()
-
- // Click learn more link
- await wrapper.find('.learn-more-link').trigger('click')
-
- expect(mockReleaseStore.handleWhatsNewSeen).toHaveBeenCalledWith('1.24.0')
- expect(wrapper.emitted('whats-new-dismissed')).toBeTruthy()
- })
-
- it('should handle cases where no release is available during close', async () => {
- mockReleaseStore.shouldShowPopup = true
- mockReleaseStore.recentRelease = null
-
- const wrapper = createWrapper()
-
- // Try to close
- await wrapper.find('.close-button').trigger('click')
-
- expect(mockReleaseStore.handleWhatsNewSeen).not.toHaveBeenCalled()
- expect(wrapper.emitted('whats-new-dismissed')).toBeTruthy()
- })
- })
-
- describe('exposed methods', () => {
- it('should expose show and hide methods', () => {
- const wrapper = createWrapper()
-
- expect(wrapper.vm.show).toBeDefined()
- expect(wrapper.vm.hide).toBeDefined()
- expect(typeof wrapper.vm.show).toBe('function')
- expect(typeof wrapper.vm.hide).toBe('function')
- })
-
- it('should show popup when show method is called', async () => {
- mockReleaseStore.shouldShowPopup = true
-
- const wrapper = createWrapper()
-
- // Initially hide it
- wrapper.vm.hide()
- await nextTick()
- expect(wrapper.find('.whats-new-popup-container').exists()).toBe(false)
-
- // Show it
- wrapper.vm.show()
- await nextTick()
- expect(wrapper.find('.whats-new-popup-container').exists()).toBe(true)
- })
-
- it('should hide popup when hide method is called', async () => {
- mockReleaseStore.shouldShowPopup = true
-
- const wrapper = createWrapper()
-
- // Initially visible
- expect(wrapper.find('.whats-new-popup-container').exists()).toBe(true)
-
- // Hide it
- wrapper.vm.hide()
- await nextTick()
- expect(wrapper.find('.whats-new-popup-container').exists()).toBe(false)
- })
- })
-
- describe('initialization', () => {
- it('should fetch releases on mount if not already loaded', async () => {
- mockReleaseStore.releases = []
- mockReleaseStore.fetchReleases.mockResolvedValue(undefined)
-
- createWrapper()
-
- // Wait for onMounted
- await nextTick()
-
- expect(mockReleaseStore.fetchReleases).toHaveBeenCalled()
- })
-
- it('should not fetch releases if already loaded', async () => {
- mockReleaseStore.releases = [
- {
- id: 1,
- project: 'comfyui_frontend',
- version: '1.24.0',
- attention: 'medium' as const,
- content: 'Content',
- published_at: '2023-01-01T00:00:00Z'
- }
- ]
- mockReleaseStore.fetchReleases.mockResolvedValue(undefined)
-
- createWrapper()
-
- // Wait for onMounted
- await nextTick()
-
- expect(mockReleaseStore.fetchReleases).not.toHaveBeenCalled()
- })
- })
-
- describe('accessibility', () => {
- it('should have proper aria-label for close button', () => {
- const mockT = vi.fn((key) => (key === 'g.close' ? 'Close' : key))
- vi.doMock('vue-i18n', () => ({
- useI18n: vi.fn(() => ({
- locale: { value: 'en' },
- t: mockT
- }))
- }))
-
- mockReleaseStore.shouldShowPopup = true
- mockReleaseStore.recentRelease = {
- id: 1,
- project: 'comfyui_frontend',
- version: '1.24.0',
- attention: 'medium',
- content: 'Content',
- published_at: '2023-01-01T00:00:00Z'
- }
-
- const wrapper = createWrapper()
-
- expect(wrapper.find('.close-button').attributes('aria-label')).toBe(
- 'Close'
- )
- })
-
- it('should have proper link attributes for external changelog', () => {
- mockReleaseStore.shouldShowPopup = true
- mockReleaseStore.recentRelease = {
- id: 1,
- project: 'comfyui_frontend',
- version: '1.24.0',
- attention: 'medium',
- content: 'Content',
- published_at: '2023-01-01T00:00:00Z'
- }
-
- const wrapper = createWrapper()
- const learnMoreLink = wrapper.find('.learn-more-link')
-
- expect(learnMoreLink.attributes('target')).toBe('_blank')
- expect(learnMoreLink.attributes('rel')).toBe('noopener,noreferrer')
- })
- })
-})
diff --git a/tests-ui/tests/composables/node/useNodePricing.test.ts b/tests-ui/tests/composables/node/useNodePricing.test.ts
index f6f45e678..ed9d5eacc 100644
--- a/tests-ui/tests/composables/node/useNodePricing.test.ts
+++ b/tests-ui/tests/composables/node/useNodePricing.test.ts
@@ -8,12 +8,7 @@ import type { IComboWidget } from '@/lib/litegraph/src/types/widgets'
function createMockNode(
nodeTypeName: string,
widgets: Array<{ name: string; value: any }> = [],
- isApiNode = true,
- inputs: Array<{
- name: string
- connected?: boolean
- useLinksArray?: boolean
- }> = []
+ isApiNode = true
): LGraphNode {
const mockWidgets = widgets.map(({ name, value }) => ({
name,
@@ -21,16 +16,7 @@ function createMockNode(
type: 'combo'
})) as IComboWidget[]
- const mockInputs =
- inputs.length > 0
- ? inputs.map(({ name, connected, useLinksArray }) =>
- useLinksArray
- ? { name, links: connected ? [1] : [] }
- : { name, link: connected ? 1 : null }
- )
- : undefined
-
- const node: any = {
+ return {
id: Math.random().toString(),
widgets: mockWidgets,
constructor: {
@@ -39,24 +25,7 @@ function createMockNode(
api_node: isApiNode
}
}
- }
-
- if (mockInputs) {
- node.inputs = mockInputs
- // Provide the common helpers some frontend code may call
- node.findInputSlot = function (portName: string) {
- return this.inputs?.findIndex((i: any) => i.name === portName) ?? -1
- }
- node.isInputConnected = function (idx: number) {
- const port = this.inputs?.[idx]
- if (!port) return false
- if (typeof port.link !== 'undefined') return port.link != null
- if (Array.isArray(port.links)) return port.links.length > 0
- return false
- }
- }
-
- return node as LGraphNode
+ } as unknown as LGraphNode
}
describe('useNodePricing', () => {
@@ -394,51 +363,34 @@ describe('useNodePricing', () => {
})
describe('dynamic pricing - IdeogramV3', () => {
- it('should return correct prices for IdeogramV3 node', () => {
+ it('should return $0.09 for Quality rendering speed', () => {
const { getNodeDisplayPrice } = useNodePricing()
+ const node = createMockNode('IdeogramV3', [
+ { name: 'rendering_speed', value: 'Quality' }
+ ])
- const testCases = [
- {
- rendering_speed: 'Quality',
- character_image: false,
- expected: '$0.09/Run'
- },
- {
- rendering_speed: 'Quality',
- character_image: true,
- expected: '$0.20/Run'
- },
- {
- rendering_speed: 'Default',
- character_image: false,
- expected: '$0.06/Run'
- },
- {
- rendering_speed: 'Default',
- character_image: true,
- expected: '$0.15/Run'
- },
- {
- rendering_speed: 'Turbo',
- character_image: false,
- expected: '$0.03/Run'
- },
- {
- rendering_speed: 'Turbo',
- character_image: true,
- expected: '$0.10/Run'
- }
- ]
+ const price = getNodeDisplayPrice(node)
+ expect(price).toBe('$0.09/Run')
+ })
- testCases.forEach(({ rendering_speed, character_image, expected }) => {
- const node = createMockNode(
- 'IdeogramV3',
- [{ name: 'rendering_speed', value: rendering_speed }],
- true,
- [{ name: 'character_image', connected: character_image }]
- )
- expect(getNodeDisplayPrice(node)).toBe(expected)
- })
+ it('should return $0.06 for Balanced rendering speed', () => {
+ const { getNodeDisplayPrice } = useNodePricing()
+ const node = createMockNode('IdeogramV3', [
+ { name: 'rendering_speed', value: 'Balanced' }
+ ])
+
+ const price = getNodeDisplayPrice(node)
+ expect(price).toBe('$0.06/Run')
+ })
+
+ it('should return $0.03 for Turbo rendering speed', () => {
+ const { getNodeDisplayPrice } = useNodePricing()
+ const node = createMockNode('IdeogramV3', [
+ { name: 'rendering_speed', value: 'Turbo' }
+ ])
+
+ const price = getNodeDisplayPrice(node)
+ expect(price).toBe('$0.03/Run')
})
it('should return range when rendering_speed widget is missing', () => {
@@ -983,11 +935,7 @@ describe('useNodePricing', () => {
const { getRelevantWidgetNames } = useNodePricing()
const widgetNames = getRelevantWidgetNames('IdeogramV3')
- expect(widgetNames).toEqual([
- 'rendering_speed',
- 'num_images',
- 'character_image'
- ])
+ expect(widgetNames).toEqual(['rendering_speed', 'num_images'])
})
})
diff --git a/tests-ui/tests/composables/nodePack/usePacksSelection.test.ts b/tests-ui/tests/composables/nodePack/usePacksSelection.test.ts
deleted file mode 100644
index 9605bd673..000000000
--- a/tests-ui/tests/composables/nodePack/usePacksSelection.test.ts
+++ /dev/null
@@ -1,378 +0,0 @@
-import { createPinia, setActivePinia } from 'pinia'
-import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
-import { ref } from 'vue'
-
-import { usePacksSelection } from '@/composables/nodePack/usePacksSelection'
-import { useComfyManagerStore } from '@/stores/comfyManagerStore'
-import type { components } from '@/types/comfyRegistryTypes'
-
-vi.mock('vue-i18n', async () => {
- const actual = await vi.importActual('vue-i18n')
- return {
- ...actual,
- useI18n: () => ({
- t: vi.fn((key) => key)
- })
- }
-})
-
-type NodePack = components['schemas']['Node']
-
-describe('usePacksSelection', () => {
- let managerStore: ReturnType
- let mockIsPackInstalled: ReturnType
-
- const createMockPack = (id: string): NodePack => ({
- id,
- name: `Pack ${id}`,
- description: `Description for pack ${id}`,
- category: 'Nodes',
- author: 'Test Author',
- license: 'MIT',
- repository: 'https://github.com/test/pack',
- tags: [],
- status: 'NodeStatusActive'
- })
-
- beforeEach(() => {
- vi.clearAllMocks()
- const pinia = createPinia()
- setActivePinia(pinia)
-
- managerStore = useComfyManagerStore()
-
- // Mock the isPackInstalled method
- mockIsPackInstalled = vi.fn()
- managerStore.isPackInstalled = mockIsPackInstalled
- })
-
- afterEach(() => {
- vi.restoreAllMocks()
- })
-
- describe('installedPacks', () => {
- it('should filter and return only installed packs', () => {
- const nodePacks = ref([
- createMockPack('pack1'),
- createMockPack('pack2'),
- createMockPack('pack3')
- ])
-
- mockIsPackInstalled.mockImplementation((id: string) => {
- return id === 'pack1' || id === 'pack3'
- })
-
- const { installedPacks } = usePacksSelection(nodePacks)
-
- expect(installedPacks.value).toHaveLength(2)
- expect(installedPacks.value[0].id).toBe('pack1')
- expect(installedPacks.value[1].id).toBe('pack3')
- expect(mockIsPackInstalled).toHaveBeenCalledTimes(3)
- })
-
- it('should return empty array when no packs are installed', () => {
- const nodePacks = ref([
- createMockPack('pack1'),
- createMockPack('pack2')
- ])
-
- mockIsPackInstalled.mockReturnValue(false)
-
- const { installedPacks } = usePacksSelection(nodePacks)
-
- expect(installedPacks.value).toHaveLength(0)
- })
-
- it('should update when nodePacks ref changes', () => {
- const nodePacks = ref([createMockPack('pack1')])
- mockIsPackInstalled.mockReturnValue(true)
-
- const { installedPacks } = usePacksSelection(nodePacks)
- expect(installedPacks.value).toHaveLength(1)
-
- // Add more packs
- nodePacks.value = [
- createMockPack('pack1'),
- createMockPack('pack2'),
- createMockPack('pack3')
- ]
-
- expect(installedPacks.value).toHaveLength(3)
- })
- })
-
- describe('notInstalledPacks', () => {
- it('should filter and return only not installed packs', () => {
- const nodePacks = ref([
- createMockPack('pack1'),
- createMockPack('pack2'),
- createMockPack('pack3')
- ])
-
- mockIsPackInstalled.mockImplementation((id: string) => {
- return id === 'pack1'
- })
-
- const { notInstalledPacks } = usePacksSelection(nodePacks)
-
- expect(notInstalledPacks.value).toHaveLength(2)
- expect(notInstalledPacks.value[0].id).toBe('pack2')
- expect(notInstalledPacks.value[1].id).toBe('pack3')
- })
-
- it('should return all packs when none are installed', () => {
- const nodePacks = ref([
- createMockPack('pack1'),
- createMockPack('pack2')
- ])
-
- mockIsPackInstalled.mockReturnValue(false)
-
- const { notInstalledPacks } = usePacksSelection(nodePacks)
-
- expect(notInstalledPacks.value).toHaveLength(2)
- })
- })
-
- describe('isAllInstalled', () => {
- it('should return true when all packs are installed', () => {
- const nodePacks = ref([
- createMockPack('pack1'),
- createMockPack('pack2')
- ])
-
- mockIsPackInstalled.mockReturnValue(true)
-
- const { isAllInstalled } = usePacksSelection(nodePacks)
-
- expect(isAllInstalled.value).toBe(true)
- })
-
- it('should return false when not all packs are installed', () => {
- const nodePacks = ref([
- createMockPack('pack1'),
- createMockPack('pack2')
- ])
-
- mockIsPackInstalled.mockImplementation((id: string) => id === 'pack1')
-
- const { isAllInstalled } = usePacksSelection(nodePacks)
-
- expect(isAllInstalled.value).toBe(false)
- })
-
- it('should return true for empty array', () => {
- const nodePacks = ref([])
-
- const { isAllInstalled } = usePacksSelection(nodePacks)
-
- expect(isAllInstalled.value).toBe(true)
- })
- })
-
- describe('isNoneInstalled', () => {
- it('should return true when no packs are installed', () => {
- const nodePacks = ref([
- createMockPack('pack1'),
- createMockPack('pack2')
- ])
-
- mockIsPackInstalled.mockReturnValue(false)
-
- const { isNoneInstalled } = usePacksSelection(nodePacks)
-
- expect(isNoneInstalled.value).toBe(true)
- })
-
- it('should return false when some packs are installed', () => {
- const nodePacks = ref([
- createMockPack('pack1'),
- createMockPack('pack2')
- ])
-
- mockIsPackInstalled.mockImplementation((id: string) => id === 'pack1')
-
- const { isNoneInstalled } = usePacksSelection(nodePacks)
-
- expect(isNoneInstalled.value).toBe(false)
- })
-
- it('should return true for empty array', () => {
- const nodePacks = ref([])
-
- const { isNoneInstalled } = usePacksSelection(nodePacks)
-
- expect(isNoneInstalled.value).toBe(true)
- })
- })
-
- describe('isMixed', () => {
- it('should return true when some but not all packs are installed', () => {
- const nodePacks = ref([
- createMockPack('pack1'),
- createMockPack('pack2'),
- createMockPack('pack3')
- ])
-
- mockIsPackInstalled.mockImplementation((id: string) => {
- return id === 'pack1' || id === 'pack2'
- })
-
- const { isMixed } = usePacksSelection(nodePacks)
-
- expect(isMixed.value).toBe(true)
- })
-
- it('should return false when all packs are installed', () => {
- const nodePacks = ref([
- createMockPack('pack1'),
- createMockPack('pack2')
- ])
-
- mockIsPackInstalled.mockReturnValue(true)
-
- const { isMixed } = usePacksSelection(nodePacks)
-
- expect(isMixed.value).toBe(false)
- })
-
- it('should return false when no packs are installed', () => {
- const nodePacks = ref([
- createMockPack('pack1'),
- createMockPack('pack2')
- ])
-
- mockIsPackInstalled.mockReturnValue(false)
-
- const { isMixed } = usePacksSelection(nodePacks)
-
- expect(isMixed.value).toBe(false)
- })
-
- it('should return false for empty array', () => {
- const nodePacks = ref