mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-04 15:10:06 +00:00
Add inline queue progress bar and text summary (#8271)
Add inline queue progress bar and summary per the new designs. This adds an inline 3px progress bar in the actionbar container (docked or floating) and a compact summary line below the top menu that follows when floating, both gated by the QPO V2 flag and hidden while the overlay is expanded. https://github.com/user-attachments/assets/da8ec7b7-35f4-4d52-a83b-15c21b484eba ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-8271-Add-inline-queue-progress-bar-and-summary-for-QPO-V2-2f16d73d36508132a6dff247f71e11e4) by [Unito](https://www.unito.io) --------- Co-authored-by: github-actions <github-actions@github.com>
This commit is contained in:
@@ -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'
|
||||
@@ -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'
|
||||
@@ -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',
|
||||
@@ -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: () => {}
|
||||
@@ -91,6 +106,7 @@ function createTask(id: string, status: JobStatus): TaskItemImpl {
|
||||
describe('TopMenuSection', () => {
|
||||
beforeEach(() => {
|
||||
vi.resetAllMocks()
|
||||
localStorage.clear()
|
||||
})
|
||||
|
||||
describe('authentication state', () => {
|
||||
@@ -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()
|
||||
|
||||
@@ -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')
|
||||
@@ -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')
|
||||
@@ -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"]')
|
||||
|
||||
@@ -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' })
|
||||
|
||||
Reference in New Issue
Block a user