Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
8c74a03
fix: open assets tab when QPO V2 enabled
benceruleanlu Jan 23, 2026
9fb11a1
fix: toggle assets tab from queue button
benceruleanlu Jan 23, 2026
fd07245
Merge remote-tracking branch 'origin/main' into fix/qpo-v2-queue-butt…
benceruleanlu Jan 23, 2026
7609554
test: cover queue button behavior by QPO setting
benceruleanlu Jan 23, 2026
c63e27f
Add inline progress text and progress bar
benceruleanlu Jan 23, 2026
76b7137
Adjust spacing
benceruleanlu Jan 23, 2026
ce86b53
Fix location when floating
benceruleanlu Jan 23, 2026
aef4ddb
Perfectly align inline progress
benceruleanlu Jan 23, 2026
c60114f
fix: anchor floating inline progress summary
benceruleanlu Jan 23, 2026
573eb8c
test: cover inline queue progress UI
benceruleanlu Jan 23, 2026
4db59b7
test: stabilize queue inline progress mock
benceruleanlu Jan 23, 2026
15229ba
[automated] Update test expectations
invalid-email-address Jan 23, 2026
4eb547b
Merge remote-tracking branch 'origin/main' into bl-inline-progress
benceruleanlu Jan 24, 2026
9a2383d
Deduplicate node title resolving
benceruleanlu Jan 24, 2026
e0cdbe3
Knip
benceruleanlu Jan 24, 2026
4234e2b
Emit progress target instead of expose
benceruleanlu Jan 24, 2026
2a9e31a
Use VueUse unrefElement
benceruleanlu Jan 24, 2026
3225214
Unify usage of resolveNodeDisplayName
benceruleanlu Jan 24, 2026
f4f0f6a
Don't use stubbed
benceruleanlu Jan 24, 2026
0dddc76
feat: add job output preview helper
benceruleanlu Jan 24, 2026
80c0ec6
test: fix job output cache instance check
benceruleanlu Jan 24, 2026
deaf27e
fix: tighten output item guard
benceruleanlu Jan 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
108 changes: 101 additions & 7 deletions src/components/TopMenuSection.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import { createTestingPinia } from '@pinia/testing'
import { mount } from '@vue/test-utils'
import type { MenuItem } from 'primevue/menuitem'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { computed, nextTick } from 'vue'
import { computed, defineComponent, h, nextTick, onMounted } from 'vue'
import type { Component } from 'vue'
import { createI18n } from 'vue-i18n'

import TopMenuSection from '@/components/TopMenuSection.vue'
Expand All @@ -14,6 +15,7 @@ import type {
} from '@/platform/remote/comfyui/jobs/jobTypes'
import { useSettingStore } from '@/platform/settings/settingStore'
import { useCommandStore } from '@/stores/commandStore'
import { useExecutionStore } from '@/stores/executionStore'
import { TaskItemImpl, useQueueStore } from '@/stores/queueStore'
import { useSidebarTabStore } from '@/stores/workspace/sidebarTabStore'
import { isElectron } from '@/utils/envUtil'
Expand All @@ -36,7 +38,17 @@ vi.mock('@/stores/firebaseAuthStore', () => ({
}))
}))

function createWrapper(pinia = createTestingPinia({ createSpy: vi.fn })) {
type WrapperOptions = {
pinia?: ReturnType<typeof createTestingPinia>
stubs?: Record<string, boolean | Component>
attachTo?: HTMLElement
}

function createWrapper({
pinia = createTestingPinia({ createSpy: vi.fn }),
stubs = {},
attachTo
}: WrapperOptions = {}) {
const i18n = createI18n({
legacy: false,
locale: 'en',
Expand All @@ -55,18 +67,21 @@ function createWrapper(pinia = createTestingPinia({ createSpy: vi.fn })) {
})

return mount(TopMenuSection, {
attachTo,
global: {
plugins: [pinia, i18n],
stubs: {
SubgraphBreadcrumb: true,
QueueProgressOverlay: true,
QueueInlineProgressSummary: true,
CurrentUserButton: true,
LoginButton: true,
ContextMenu: {
name: 'ContextMenu',
props: ['model'],
template: '<div />'
}
},
...stubs
},
directives: {
tooltip: () => {}
Expand All @@ -91,6 +106,7 @@ function createTask(id: string, status: JobStatus): TaskItemImpl {
describe('TopMenuSection', () => {
beforeEach(() => {
vi.resetAllMocks()
localStorage.clear()
})

describe('authentication state', () => {
Expand Down Expand Up @@ -151,7 +167,7 @@ describe('TopMenuSection', () => {
vi.mocked(settingStore.get).mockImplementation((key) =>
key === 'Comfy.Queue.QPOV2' ? true : undefined
)
const wrapper = createWrapper(pinia)
const wrapper = createWrapper({ pinia })

await nextTick()

Expand All @@ -169,7 +185,7 @@ describe('TopMenuSection', () => {
vi.mocked(settingStore.get).mockImplementation((key) =>
key === 'Comfy.Queue.QPOV2' ? false : undefined
)
const wrapper = createWrapper(pinia)
const wrapper = createWrapper({ pinia })
const commandStore = useCommandStore(pinia)

await wrapper.find('[data-testid="queue-overlay-toggle"]').trigger('click')
Expand All @@ -185,7 +201,7 @@ describe('TopMenuSection', () => {
vi.mocked(settingStore.get).mockImplementation((key) =>
key === 'Comfy.Queue.QPOV2' ? true : undefined
)
const wrapper = createWrapper(pinia)
const wrapper = createWrapper({ pinia })
const sidebarTabStore = useSidebarTabStore(pinia)

await wrapper.find('[data-testid="queue-overlay-toggle"]').trigger('click')
Expand All @@ -199,7 +215,7 @@ describe('TopMenuSection', () => {
vi.mocked(settingStore.get).mockImplementation((key) =>
key === 'Comfy.Queue.QPOV2' ? true : undefined
)
const wrapper = createWrapper(pinia)
const wrapper = createWrapper({ pinia })
const sidebarTabStore = useSidebarTabStore(pinia)
const toggleButton = wrapper.find('[data-testid="queue-overlay-toggle"]')

Expand All @@ -210,6 +226,84 @@ describe('TopMenuSection', () => {
expect(sidebarTabStore.activeSidebarTabId).toBe(null)
})

describe('inline progress summary', () => {
const configureSettings = (
pinia: ReturnType<typeof createTestingPinia>,
qpoV2Enabled: boolean
) => {
const settingStore = useSettingStore(pinia)
vi.mocked(settingStore.get).mockImplementation((key) => {
if (key === 'Comfy.Queue.QPOV2') return qpoV2Enabled
if (key === 'Comfy.UseNewMenu') return 'Top'
return undefined
})
}

it('renders inline progress summary when QPO V2 is enabled', async () => {
const pinia = createTestingPinia({ createSpy: vi.fn })
configureSettings(pinia, true)

const wrapper = createWrapper({ pinia })

await nextTick()

expect(
wrapper.findComponent({ name: 'QueueInlineProgressSummary' }).exists()
).toBe(true)
})

it('does not render inline progress summary when QPO V2 is disabled', async () => {
const pinia = createTestingPinia({ createSpy: vi.fn })
configureSettings(pinia, false)

const wrapper = createWrapper({ pinia })

await nextTick()

expect(
wrapper.findComponent({ name: 'QueueInlineProgressSummary' }).exists()
).toBe(false)
})

it('teleports inline progress summary when actionbar is floating', async () => {
localStorage.setItem('Comfy.MenuPosition.Docked', 'false')
const actionbarTarget = document.createElement('div')
document.body.appendChild(actionbarTarget)
const pinia = createTestingPinia({ createSpy: vi.fn })
configureSettings(pinia, true)
const executionStore = useExecutionStore(pinia)
executionStore.activePromptId = 'prompt-1'

const ComfyActionbarStub = defineComponent({
name: 'ComfyActionbar',
setup(_, { emit }) {
onMounted(() => {
emit('update:progressTarget', actionbarTarget)
})
return () => h('div')
}
})

const wrapper = createWrapper({
pinia,
attachTo: document.body,
stubs: {
ComfyActionbar: ComfyActionbarStub,
QueueInlineProgressSummary: false
}
})

try {
await nextTick()

expect(actionbarTarget.querySelector('[role="status"]')).not.toBeNull()
} finally {
wrapper.unmount()
actionbarTarget.remove()
}
})
})

it('disables the clear queue context menu item when no queued jobs exist', () => {
const wrapper = createWrapper()
const menu = wrapper.findComponent({ name: 'ContextMenu' })
Expand Down
Loading