mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-03-09 23:20:04 +00:00
## Summary - move queue batch controls to the left of the run button - align run control styling to the Figma queue modal spec using PrimeVue PT/Tailwind (secondary background on batch + dropdown, primary run button) - normalize control heights to match actionbar buttons and tighten dropdown hit area - update run typography/spacing and replace all three chevrons (dropdown + batch up/down) with the requested SVG Design: https://www.figma.com/design/LVilZgHGk5RwWOkVN6yCEK/Queue-Progress-Modal?node-id=3845-23904&m=dev <img width="303" height="122" alt="image" src="https://github.com/user-attachments/assets/4ed80ee7-3ceb-4512-96ce-f55ec6da835e" /> ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-9134-fix-align-run-controls-with-queue-modal-design-3106d73d36508160afcedbcfe4b98291) by [Unito](https://www.unito.io) --------- Co-authored-by: github-actions <github-actions@github.com> Co-authored-by: GitHub Action <action@github.com>
189 lines
5.7 KiB
TypeScript
189 lines
5.7 KiB
TypeScript
import { createTestingPinia } from '@pinia/testing'
|
|
import { mount } from '@vue/test-utils'
|
|
import { describe, expect, it, vi } from 'vitest'
|
|
import { nextTick } from 'vue'
|
|
import { createI18n } from 'vue-i18n'
|
|
|
|
import type {
|
|
JobListItem,
|
|
JobStatus
|
|
} from '@/platform/remote/comfyui/jobs/jobTypes'
|
|
import { useCommandStore } from '@/stores/commandStore'
|
|
import {
|
|
TaskItemImpl,
|
|
useQueueSettingsStore,
|
|
useQueueStore
|
|
} from '@/stores/queueStore'
|
|
|
|
import ComfyQueueButton from './ComfyQueueButton.vue'
|
|
|
|
vi.mock('@/platform/distribution/types', () => ({
|
|
isCloud: false
|
|
}))
|
|
|
|
vi.mock('@/platform/telemetry', () => ({
|
|
useTelemetry: () => null
|
|
}))
|
|
|
|
vi.mock('@/workbench/extensions/manager/utils/graphHasMissingNodes', () => ({
|
|
graphHasMissingNodes: () => false
|
|
}))
|
|
|
|
vi.mock('@/scripts/app', () => ({
|
|
app: {
|
|
rootGraph: {}
|
|
}
|
|
}))
|
|
|
|
vi.mock('@/stores/workspaceStore', () => ({
|
|
useWorkspaceStore: () => ({
|
|
shiftDown: false
|
|
})
|
|
}))
|
|
|
|
const BatchCountEditStub = {
|
|
template: '<div data-testid="batch-count-edit" />'
|
|
}
|
|
|
|
const i18n = createI18n({
|
|
legacy: false,
|
|
locale: 'en',
|
|
messages: {
|
|
en: {
|
|
menu: {
|
|
run: 'Run',
|
|
disabledTooltip: 'Disabled tooltip',
|
|
onChange: 'On Change',
|
|
onChangeTooltip: 'On change tooltip',
|
|
instant: 'Instant',
|
|
instantTooltip: 'Instant tooltip',
|
|
stopRunInstant: 'Stop Run (Instant)',
|
|
stopRunInstantTooltip: 'Stop running',
|
|
runWorkflow: 'Run workflow',
|
|
runWorkflowFront: 'Run workflow front',
|
|
runWorkflowDisabled: 'Run workflow disabled'
|
|
}
|
|
}
|
|
}
|
|
})
|
|
|
|
function createTask(id: string, status: JobStatus): TaskItemImpl {
|
|
const job: JobListItem = {
|
|
id,
|
|
status,
|
|
create_time: Date.now(),
|
|
priority: 1
|
|
}
|
|
|
|
return new TaskItemImpl(job)
|
|
}
|
|
|
|
function createWrapper() {
|
|
const pinia = createTestingPinia({ createSpy: vi.fn })
|
|
|
|
return mount(ComfyQueueButton, {
|
|
global: {
|
|
plugins: [pinia, i18n],
|
|
directives: {
|
|
tooltip: () => {}
|
|
},
|
|
stubs: {
|
|
BatchCountEdit: BatchCountEditStub,
|
|
DropdownMenuRoot: { template: '<div><slot /></div>' },
|
|
DropdownMenuTrigger: { template: '<div><slot /></div>' },
|
|
DropdownMenuPortal: { template: '<div><slot /></div>' },
|
|
DropdownMenuContent: { template: '<div><slot /></div>' },
|
|
DropdownMenuItem: { template: '<div><slot /></div>' }
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
describe('ComfyQueueButton', () => {
|
|
it('renders the batch count control before the run button', () => {
|
|
const wrapper = createWrapper()
|
|
const controls = wrapper.get('.queue-button-group').element.children
|
|
|
|
expect(controls[0]?.getAttribute('data-testid')).toBe('batch-count-edit')
|
|
expect(controls[1]?.getAttribute('data-testid')).toBe('queue-button')
|
|
})
|
|
|
|
it('keeps the run instant presentation while idle even with active jobs', async () => {
|
|
const wrapper = createWrapper()
|
|
const queueSettingsStore = useQueueSettingsStore()
|
|
const queueStore = useQueueStore()
|
|
|
|
queueSettingsStore.mode = 'instant-idle'
|
|
queueStore.runningTasks = [createTask('run-1', 'in_progress')]
|
|
await nextTick()
|
|
|
|
const queueButton = wrapper.get('[data-testid="queue-button"]')
|
|
|
|
expect(queueButton.text()).toContain('Run (Instant)')
|
|
expect(queueButton.attributes('data-variant')).toBe('primary')
|
|
expect(wrapper.find('.icon-\\[lucide--fast-forward\\]').exists()).toBe(true)
|
|
})
|
|
|
|
it('switches to stop presentation when instant mode is armed', async () => {
|
|
const wrapper = createWrapper()
|
|
const queueSettingsStore = useQueueSettingsStore()
|
|
|
|
queueSettingsStore.mode = 'instant-running'
|
|
await nextTick()
|
|
|
|
const queueButton = wrapper.get('[data-testid="queue-button"]')
|
|
|
|
expect(queueButton.text()).toContain('Stop Run (Instant)')
|
|
expect(queueButton.attributes('data-variant')).toBe('destructive')
|
|
expect(wrapper.find('.icon-\\[lucide--square\\]').exists()).toBe(true)
|
|
})
|
|
|
|
it('disarms instant mode without interrupting even when jobs are active', async () => {
|
|
const wrapper = createWrapper()
|
|
const queueSettingsStore = useQueueSettingsStore()
|
|
const queueStore = useQueueStore()
|
|
const commandStore = useCommandStore()
|
|
|
|
queueSettingsStore.mode = 'instant-running'
|
|
queueStore.runningTasks = [createTask('run-1', 'in_progress')]
|
|
await nextTick()
|
|
|
|
await wrapper.get('[data-testid="queue-button"]').trigger('click')
|
|
await nextTick()
|
|
|
|
expect(queueSettingsStore.mode).toBe('instant-idle')
|
|
const queueButtonWhileStopping = wrapper.get('[data-testid="queue-button"]')
|
|
expect(queueButtonWhileStopping.text()).toContain('Run (Instant)')
|
|
expect(queueButtonWhileStopping.attributes('data-variant')).toBe('primary')
|
|
expect(wrapper.find('.icon-\\[lucide--fast-forward\\]').exists()).toBe(true)
|
|
|
|
expect(commandStore.execute).not.toHaveBeenCalled()
|
|
|
|
const queueButton = wrapper.get('[data-testid="queue-button"]')
|
|
expect(queueSettingsStore.mode).toBe('instant-idle')
|
|
expect(queueButton.text()).toContain('Run (Instant)')
|
|
expect(queueButton.attributes('data-variant')).toBe('primary')
|
|
expect(wrapper.find('.icon-\\[lucide--fast-forward\\]').exists()).toBe(true)
|
|
})
|
|
|
|
it('activates instant running mode when queueing again', async () => {
|
|
const wrapper = createWrapper()
|
|
const queueSettingsStore = useQueueSettingsStore()
|
|
const commandStore = useCommandStore()
|
|
|
|
queueSettingsStore.mode = 'instant-idle'
|
|
await nextTick()
|
|
|
|
await wrapper.get('[data-testid="queue-button"]').trigger('click')
|
|
await nextTick()
|
|
|
|
expect(queueSettingsStore.mode).toBe('instant-running')
|
|
expect(commandStore.execute).toHaveBeenCalledWith('Comfy.QueuePrompt', {
|
|
metadata: {
|
|
subscribe_to_run: false,
|
|
trigger_source: 'button'
|
|
}
|
|
})
|
|
})
|
|
})
|