Skip to content
Closed
Show file tree
Hide file tree
Changes from 37 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
71c6da2
Readd cancel button
benceruleanlu Dec 6, 2025
b790aaf
Add new N queued toggle
benceruleanlu Dec 6, 2025
e65fc56
Remove active state
benceruleanlu Dec 7, 2025
ca084c3
Fix text size
benceruleanlu Dec 7, 2025
5c106bd
Add progress bar embedded into top menu section
benceruleanlu Dec 7, 2025
da4b241
Remove unused QueueOverlayActive files
benceruleanlu Dec 7, 2025
5e1dc91
Extract to own component file
benceruleanlu Dec 7, 2025
d965fb0
Add progress bars and progress text
benceruleanlu Dec 7, 2025
05d2cae
Add close button back to queue progress overlay
benceruleanlu Dec 7, 2025
99019cf
Align expanded QPO to new design
benceruleanlu Dec 7, 2025
0a5fc8b
Delete unused file
benceruleanlu Dec 7, 2025
c4b53c1
Make job list button visual seperation change
benceruleanlu Dec 8, 2025
48aa3d9
Remove unused event
benceruleanlu Dec 8, 2025
87abea9
Merge remote-tracking branch 'origin/main' into bl-semantic-jellyfish
benceruleanlu Dec 9, 2025
ad44241
[automated] Update test expectations
invalid-email-address Dec 9, 2025
73741df
fix: lint issues and unused exports
benceruleanlu Dec 9, 2025
8b4f174
refactor(actionbar): move cancel and queue buttons into draggable con…
benceruleanlu Dec 9, 2025
f0b9d28
fix(queue): rename clearQueued translation key to clearQueue
benceruleanlu Dec 9, 2025
b428892
Add tests
benceruleanlu Dec 9, 2025
ab820bf
Readd missing DTID
benceruleanlu Dec 9, 2025
f6ffb1f
Fix actionbar digit shifting
benceruleanlu Dec 9, 2025
db80292
Fix text expectation after digit shift change
benceruleanlu Dec 9, 2025
a5b5fe8
Merge remote-tracking branch 'origin/main' into bl-semantic-jellyfish
benceruleanlu Dec 9, 2025
a48360d
nit
benceruleanlu Dec 9, 2025
96893f7
nit
benceruleanlu Dec 9, 2025
cbbf3eb
[automated] Update test expectations
invalid-email-address Dec 9, 2025
f7d3a8c
nit
benceruleanlu Dec 9, 2025
8fdf355
nit
benceruleanlu Dec 9, 2025
38b589c
nit
benceruleanlu Dec 9, 2025
3ce8cca
nit
benceruleanlu Dec 9, 2025
3fe2d39
nit
benceruleanlu Dec 9, 2025
ae1d6f9
nit
benceruleanlu Dec 9, 2025
b277cbf
nit
benceruleanlu Dec 9, 2025
b21cf86
Merge branch 'bl-semantic-jellyfish' of https://github.com/Comfy-Org/…
benceruleanlu Dec 9, 2025
8bec2df
nit
benceruleanlu Dec 9, 2025
268ab1d
Implement progress bar on actionbar
benceruleanlu Dec 10, 2025
36fda56
Implement progress text on actionbar
benceruleanlu Dec 10, 2025
33626a5
Fix menu position docked issue
benceruleanlu Dec 10, 2025
d963b8e
Widen drop zone
benceruleanlu Dec 10, 2025
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
3 changes: 3 additions & 0 deletions browser_tests/fixtures/ComfyPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { ComfyTemplates } from '../helpers/templates'
import { ComfyMouse } from './ComfyMouse'
import { VueNodeHelpers } from './VueNodeHelpers'
import { ComfyNodeSearchBox } from './components/ComfyNodeSearchBox'
import { QueueList } from './components/QueueList'
import { SettingDialog } from './components/SettingDialog'
import {
NodeLibrarySidebarTab,
Expand Down Expand Up @@ -151,6 +152,7 @@ export class ComfyPage {

// Components
public readonly searchBox: ComfyNodeSearchBox
public readonly queueList: QueueList
public readonly menu: ComfyMenu
public readonly actionbar: ComfyActionbar
public readonly templates: ComfyTemplates
Expand Down Expand Up @@ -183,6 +185,7 @@ export class ComfyPage {
this.visibleToasts = page.locator('.p-toast-message:visible')

this.searchBox = new ComfyNodeSearchBox(page)
this.queueList = new QueueList(page)
this.menu = new ComfyMenu(page)
this.actionbar = new ComfyActionbar(page)
this.templates = new ComfyTemplates(page)
Expand Down
57 changes: 57 additions & 0 deletions browser_tests/fixtures/components/QueueList.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import type { Page } from '@playwright/test'
import { expect } from '@playwright/test'

export class QueueList {
constructor(public readonly page: Page) {}

get toggleButton() {
return this.page.getByTestId('queue-toggle-button')
}

get inlineProgress() {
return this.page.getByTestId('queue-inline-progress')
}

get overlay() {
return this.page.getByTestId('queue-overlay')
}

get closeButton() {
return this.page.getByTestId('queue-overlay-close-button')
}

get jobItems() {
return this.page.getByTestId('queue-job-item')
}

get clearHistoryButton() {
return this.page.getByRole('button', { name: /Clear History/i })
}

async open() {
if (!(await this.overlay.isVisible())) {
await this.toggleButton.click()
await expect(this.overlay).toBeVisible()
}
}

async close() {
if (await this.overlay.isVisible()) {
await this.closeButton.click()
await expect(this.overlay).not.toBeVisible()
}
}

async getJobCount(state?: string) {
if (state) {
return await this.page
.locator(`[data-testid="queue-job-item"][data-job-state="${state}"]`)
.count()
}
return await this.jobItems.count()
}

getJobAction(actionKey: string) {
return this.page.getByTestId(`job-action-${actionKey}`)
}
}
6 changes: 5 additions & 1 deletion browser_tests/fixtures/ws.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import { test as base } from '@playwright/test'

import type { StatusWsMessage } from '../../src/schemas/apiSchema'

export type WsMessage = { type: 'status'; data: StatusWsMessage }

export const webSocketFixture = base.extend<{
ws: { trigger(data: any, url?: string): Promise<void> }
ws: { trigger(data: WsMessage, url?: string): Promise<void> }
}>({
ws: [
async ({ page }, use) => {
Expand Down
12 changes: 7 additions & 5 deletions browser_tests/tests/actionbar.spec.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import type { Response } from '@playwright/test'
import { expect, mergeTests } from '@playwright/test'

import type { StatusWsMessage } from '../../src/schemas/apiSchema.ts'
import { comfyPageFixture } from '../fixtures/ComfyPage.ts'
import { webSocketFixture } from '../fixtures/ws.ts'
import { comfyPageFixture } from '../fixtures/ComfyPage'
import { webSocketFixture } from '../fixtures/ws'
import type { WsMessage } from '../fixtures/ws'

const test = mergeTests(comfyPageFixture, webSocketFixture)

Expand Down Expand Up @@ -61,7 +61,7 @@ test.describe('Actionbar', () => {

// Trigger a status websocket message
const triggerStatus = async (queueSize: number) => {
await ws.trigger({
const message = {
type: 'status',
data: {
status: {
Expand All @@ -70,7 +70,9 @@ test.describe('Actionbar', () => {
}
}
}
} as StatusWsMessage)
} satisfies WsMessage

await ws.trigger(message)
}

// Extract the width from the queue response
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
157 changes: 157 additions & 0 deletions browser_tests/tests/queue/queueList.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
import { expect, mergeTests } from '@playwright/test'

import type { ComfyPage } from '../../fixtures/ComfyPage'
import { comfyPageFixture } from '../../fixtures/ComfyPage'
import { webSocketFixture } from '../../fixtures/ws'
import type { WsMessage } from '../../fixtures/ws'

const test = mergeTests(comfyPageFixture, webSocketFixture)

type QueueState = {
running: QueueJob[]
pending: QueueJob[]
}

type QueueJob = [
string,
string,
Record<string, unknown>,
Record<string, unknown>,
string[]
]

type QueueController = {
state: QueueState
sync: (
ws: { trigger(data: WsMessage, url?: string): Promise<void> },
nextState: Partial<QueueState>
) => Promise<void>
}

test.describe('Queue UI', () => {
let queue: QueueController

test.beforeEach(async ({ comfyPage }) => {
await comfyPage.page.route('**/api/prompt', async (route) => {
await route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({
prompt_id: 'mock-prompt-id',
number: 1,
node_errors: {}
})
})
})

// Mock history to avoid pulling real data
await comfyPage.page.route('**/api/history**', async (route) => {
await route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({ History: [] })
})
})

queue = await createQueueController(comfyPage)
})

test('toggles overlay and updates count from status events', async ({
comfyPage,
ws
}) => {
await queue.sync(ws, { running: [], pending: [] })

await expect(comfyPage.queueList.toggleButton).toContainText('0')
await expect(comfyPage.queueList.toggleButton).toContainText(/queued/i)
await expect(comfyPage.queueList.overlay).toBeHidden()

await queue.sync(ws, {
pending: [queueJob('1', 'mock-pending', 'client-a')]
})

await expect(comfyPage.queueList.toggleButton).toContainText('1')
await expect(comfyPage.queueList.toggleButton).toContainText(/queued/i)

await comfyPage.queueList.open()
await expect(comfyPage.queueList.overlay).toBeVisible()
await expect(comfyPage.queueList.jobItems).toHaveCount(1)

await comfyPage.queueList.close()
await expect(comfyPage.queueList.overlay).toBeHidden()
})
Comment on lines +59 to +82
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Good coverage of overlay toggle and count; consider asserting inline progress as well

This test validates the count text, overlay visibility, and item rendering end‑to‑end via mocked WS + /api/queue, which is great. To more directly cover the new inline progress indicator you might also assert on whatever element queueList.inlineProgress exposes (e.g., its visible count or label) so regressions in the top‑menu indicator itself are caught, not just the toggle button text.

🤖 Prompt for AI Agents
In browser_tests/tests/queue/queueList.spec.ts around lines 59-82, the test
verifies the toggle button count and overlay but omits assertions for the new
inline progress indicator; add assertions using
comfyPage.queueList.inlineProgress to assert its text/label reflects the queued
count (e.g., contains '0' and /queued/i initially, then '1' and /queued/i after
the pending update) and assert visibility/state as appropriate so the top-menu
indicator is validated alongside the toggle button and overlay.


test('displays running and pending jobs via status updates', async ({
comfyPage,
ws
}) => {
await queue.sync(ws, {
running: [queueJob('2', 'mock-running', 'client-b')],
pending: [queueJob('3', 'mock-pending', 'client-c')]
})

await comfyPage.queueList.open()
await expect(comfyPage.queueList.jobItems).toHaveCount(2)

const firstJob = comfyPage.queueList.jobItems.first()
await firstJob.hover()

const cancelAction = firstJob
.getByTestId('job-action-cancel-running')
.or(firstJob.getByTestId('job-action-cancel-hover'))

await expect(cancelAction).toBeVisible()
})
})

const queueJob = (
queueIndex: string,
promptId: string,
clientId: string
): QueueJob => [
queueIndex,
promptId,
{ client_id: clientId },
{ class_type: 'Note' },
['output']
]

const createQueueController = async (
comfyPage: ComfyPage
): Promise<QueueController> => {
const state: QueueState = { running: [], pending: [] }

// Single queue handler reads the latest in-memory state
await comfyPage.page.route('**/api/queue', async (route) => {
await route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({
queue_running: state.running,
queue_pending: state.pending
})
})
})

const sync = async (
ws: { trigger(data: WsMessage, url?: string): Promise<void> },
nextState: Partial<QueueState>
) => {
if (nextState.running) state.running = nextState.running
if (nextState.pending) state.pending = nextState.pending

const total = state.running.length + state.pending.length
const queueResponse = comfyPage.page.waitForResponse('**/api/queue')

await ws.trigger({
type: 'status',
data: {
status: { exec_info: { queue_remaining: total } }
}
})

await queueResponse
}

return { state, sync }
}
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.
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.
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.
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.
Loading