diff --git a/browser_tests/fixtures/ComfyPage.ts b/browser_tests/fixtures/ComfyPage.ts index 73a8b84618..7304faf734 100644 --- a/browser_tests/fixtures/ComfyPage.ts +++ b/browser_tests/fixtures/ComfyPage.ts @@ -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, @@ -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 @@ -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) diff --git a/browser_tests/fixtures/components/QueueList.ts b/browser_tests/fixtures/components/QueueList.ts new file mode 100644 index 0000000000..453ef0765b --- /dev/null +++ b/browser_tests/fixtures/components/QueueList.ts @@ -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}`) + } +} diff --git a/browser_tests/fixtures/ws.ts b/browser_tests/fixtures/ws.ts index f1ab1a538c..7606f73d20 100644 --- a/browser_tests/fixtures/ws.ts +++ b/browser_tests/fixtures/ws.ts @@ -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 } + ws: { trigger(data: WsMessage, url?: string): Promise } }>({ ws: [ async ({ page }, use) => { diff --git a/browser_tests/tests/actionbar.spec.ts b/browser_tests/tests/actionbar.spec.ts index bd086b4610..23e327b5cb 100644 --- a/browser_tests/tests/actionbar.spec.ts +++ b/browser_tests/tests/actionbar.spec.ts @@ -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) @@ -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: { @@ -70,7 +70,9 @@ test.describe('Actionbar', () => { } } } - } as StatusWsMessage) + } satisfies WsMessage + + await ws.trigger(message) } // Extract the width from the queue response diff --git a/browser_tests/tests/interaction.spec.ts-snapshots/prompt-dialog-closed-chromium-linux.png b/browser_tests/tests/interaction.spec.ts-snapshots/prompt-dialog-closed-chromium-linux.png index 0c57f7db1d..6c0bdcc603 100644 Binary files a/browser_tests/tests/interaction.spec.ts-snapshots/prompt-dialog-closed-chromium-linux.png and b/browser_tests/tests/interaction.spec.ts-snapshots/prompt-dialog-closed-chromium-linux.png differ diff --git a/browser_tests/tests/queue/queueList.spec.ts b/browser_tests/tests/queue/queueList.spec.ts new file mode 100644 index 0000000000..e0bb04a9ce --- /dev/null +++ b/browser_tests/tests/queue/queueList.spec.ts @@ -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, + Record, + string[] +] + +type QueueController = { + state: QueueState + sync: ( + ws: { trigger(data: WsMessage, url?: string): Promise }, + nextState: Partial + ) => Promise +} + +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() + }) + + 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 => { + 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 }, + nextState: Partial + ) => { + 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 } +} diff --git a/browser_tests/tests/vueNodes/groups/groups.spec.ts-snapshots/vue-groups-create-group-chromium-linux.png b/browser_tests/tests/vueNodes/groups/groups.spec.ts-snapshots/vue-groups-create-group-chromium-linux.png index 911f948438..d2009ca4a8 100644 Binary files a/browser_tests/tests/vueNodes/groups/groups.spec.ts-snapshots/vue-groups-create-group-chromium-linux.png and b/browser_tests/tests/vueNodes/groups/groups.spec.ts-snapshots/vue-groups-create-group-chromium-linux.png differ diff --git a/browser_tests/tests/vueNodes/groups/groups.spec.ts-snapshots/vue-groups-fit-to-contents-chromium-linux.png b/browser_tests/tests/vueNodes/groups/groups.spec.ts-snapshots/vue-groups-fit-to-contents-chromium-linux.png index 4ee0be80a4..18a69b299c 100644 Binary files a/browser_tests/tests/vueNodes/groups/groups.spec.ts-snapshots/vue-groups-fit-to-contents-chromium-linux.png and b/browser_tests/tests/vueNodes/groups/groups.spec.ts-snapshots/vue-groups-fit-to-contents-chromium-linux.png differ diff --git a/browser_tests/tests/vueNodes/interactions/canvas/pan.spec.ts-snapshots/vue-nodes-paned-with-touch-mobile-chrome-linux.png b/browser_tests/tests/vueNodes/interactions/canvas/pan.spec.ts-snapshots/vue-nodes-paned-with-touch-mobile-chrome-linux.png index 936b8206e7..4c44e29ca1 100644 Binary files a/browser_tests/tests/vueNodes/interactions/canvas/pan.spec.ts-snapshots/vue-nodes-paned-with-touch-mobile-chrome-linux.png and b/browser_tests/tests/vueNodes/interactions/canvas/pan.spec.ts-snapshots/vue-nodes-paned-with-touch-mobile-chrome-linux.png differ diff --git a/browser_tests/tests/vueNodes/interactions/canvas/zoom.spec.ts-snapshots/zoomed-in-ctrl-shift-chromium-linux.png b/browser_tests/tests/vueNodes/interactions/canvas/zoom.spec.ts-snapshots/zoomed-in-ctrl-shift-chromium-linux.png index 533acb539c..8d5e390072 100644 Binary files a/browser_tests/tests/vueNodes/interactions/canvas/zoom.spec.ts-snapshots/zoomed-in-ctrl-shift-chromium-linux.png and b/browser_tests/tests/vueNodes/interactions/canvas/zoom.spec.ts-snapshots/zoomed-in-ctrl-shift-chromium-linux.png differ diff --git a/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-dragging-link-chromium-linux.png b/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-dragging-link-chromium-linux.png index 5498359063..8b58d24f3b 100644 Binary files a/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-dragging-link-chromium-linux.png and b/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-dragging-link-chromium-linux.png differ diff --git a/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-input-drag-ctrl-alt-chromium-linux.png b/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-input-drag-ctrl-alt-chromium-linux.png index b9f00d5150..027452fac0 100644 Binary files a/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-input-drag-ctrl-alt-chromium-linux.png and b/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-input-drag-ctrl-alt-chromium-linux.png differ diff --git a/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-input-drag-reuses-origin-chromium-linux.png b/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-input-drag-reuses-origin-chromium-linux.png index 756d1dcd10..e7d97336b9 100644 Binary files a/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-input-drag-reuses-origin-chromium-linux.png and b/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-input-drag-reuses-origin-chromium-linux.png differ diff --git a/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-reroute-input-drag-chromium-linux.png b/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-reroute-input-drag-chromium-linux.png index b57d9c3768..525bb2a5b4 100644 Binary files a/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-reroute-input-drag-chromium-linux.png and b/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-reroute-input-drag-chromium-linux.png differ diff --git a/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-reroute-output-shift-drag-chromium-linux.png b/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-reroute-output-shift-drag-chromium-linux.png index e80c363a36..abe3803b0f 100644 Binary files a/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-reroute-output-shift-drag-chromium-linux.png and b/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-reroute-output-shift-drag-chromium-linux.png differ diff --git a/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-shift-output-multi-link-chromium-linux.png b/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-shift-output-multi-link-chromium-linux.png index 5080c46a7b..80bcd5d7a9 100644 Binary files a/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-shift-output-multi-link-chromium-linux.png and b/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-shift-output-multi-link-chromium-linux.png differ diff --git a/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-snap-to-node-chromium-linux.png b/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-snap-to-node-chromium-linux.png index 5ee7ea326d..de195eb047 100644 Binary files a/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-snap-to-node-chromium-linux.png and b/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-snap-to-node-chromium-linux.png differ diff --git a/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-snap-to-slot-chromium-linux.png b/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-snap-to-slot-chromium-linux.png index 81a3106ef1..9af04f4a4b 100644 Binary files a/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-snap-to-slot-chromium-linux.png and b/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-snap-to-slot-chromium-linux.png differ diff --git a/browser_tests/tests/vueNodes/interactions/node/move.spec.ts-snapshots/vue-node-moved-node-chromium-linux.png b/browser_tests/tests/vueNodes/interactions/node/move.spec.ts-snapshots/vue-node-moved-node-chromium-linux.png index 965838d6e4..ae0b5d5e03 100644 Binary files a/browser_tests/tests/vueNodes/interactions/node/move.spec.ts-snapshots/vue-node-moved-node-chromium-linux.png and b/browser_tests/tests/vueNodes/interactions/node/move.spec.ts-snapshots/vue-node-moved-node-chromium-linux.png differ diff --git a/browser_tests/tests/vueNodes/interactions/node/move.spec.ts-snapshots/vue-node-moved-node-touch-mobile-chrome-linux.png b/browser_tests/tests/vueNodes/interactions/node/move.spec.ts-snapshots/vue-node-moved-node-touch-mobile-chrome-linux.png index 46a697200f..c47a61e6f7 100644 Binary files a/browser_tests/tests/vueNodes/interactions/node/move.spec.ts-snapshots/vue-node-moved-node-touch-mobile-chrome-linux.png and b/browser_tests/tests/vueNodes/interactions/node/move.spec.ts-snapshots/vue-node-moved-node-touch-mobile-chrome-linux.png differ diff --git a/browser_tests/tests/vueNodes/nodeStates/bypass.spec.ts-snapshots/vue-node-bypassed-state-chromium-linux.png b/browser_tests/tests/vueNodes/nodeStates/bypass.spec.ts-snapshots/vue-node-bypassed-state-chromium-linux.png index 6f7d0ebb8c..5022e45a79 100644 Binary files a/browser_tests/tests/vueNodes/nodeStates/bypass.spec.ts-snapshots/vue-node-bypassed-state-chromium-linux.png and b/browser_tests/tests/vueNodes/nodeStates/bypass.spec.ts-snapshots/vue-node-bypassed-state-chromium-linux.png differ diff --git a/browser_tests/tests/vueNodes/nodeStates/colors.spec.ts-snapshots/vue-node-custom-color-blue-chromium-linux.png b/browser_tests/tests/vueNodes/nodeStates/colors.spec.ts-snapshots/vue-node-custom-color-blue-chromium-linux.png index 71844b3a32..ab3c5254a9 100644 Binary files a/browser_tests/tests/vueNodes/nodeStates/colors.spec.ts-snapshots/vue-node-custom-color-blue-chromium-linux.png and b/browser_tests/tests/vueNodes/nodeStates/colors.spec.ts-snapshots/vue-node-custom-color-blue-chromium-linux.png differ diff --git a/browser_tests/tests/vueNodes/nodeStates/colors.spec.ts-snapshots/vue-node-custom-colors-dark-all-colors-chromium-linux.png b/browser_tests/tests/vueNodes/nodeStates/colors.spec.ts-snapshots/vue-node-custom-colors-dark-all-colors-chromium-linux.png index 2e0fa61dcb..d478bbc210 100644 Binary files a/browser_tests/tests/vueNodes/nodeStates/colors.spec.ts-snapshots/vue-node-custom-colors-dark-all-colors-chromium-linux.png and b/browser_tests/tests/vueNodes/nodeStates/colors.spec.ts-snapshots/vue-node-custom-colors-dark-all-colors-chromium-linux.png differ diff --git a/browser_tests/tests/vueNodes/nodeStates/colors.spec.ts-snapshots/vue-node-custom-colors-light-all-colors-chromium-linux.png b/browser_tests/tests/vueNodes/nodeStates/colors.spec.ts-snapshots/vue-node-custom-colors-light-all-colors-chromium-linux.png index 5b9dc013d8..34818a61b0 100644 Binary files a/browser_tests/tests/vueNodes/nodeStates/colors.spec.ts-snapshots/vue-node-custom-colors-light-all-colors-chromium-linux.png and b/browser_tests/tests/vueNodes/nodeStates/colors.spec.ts-snapshots/vue-node-custom-colors-light-all-colors-chromium-linux.png differ diff --git a/browser_tests/tests/vueNodes/nodeStates/mute.spec.ts-snapshots/vue-node-muted-state-chromium-linux.png b/browser_tests/tests/vueNodes/nodeStates/mute.spec.ts-snapshots/vue-node-muted-state-chromium-linux.png index b5224984b6..d8241767ed 100644 Binary files a/browser_tests/tests/vueNodes/nodeStates/mute.spec.ts-snapshots/vue-node-muted-state-chromium-linux.png and b/browser_tests/tests/vueNodes/nodeStates/mute.spec.ts-snapshots/vue-node-muted-state-chromium-linux.png differ diff --git a/browser_tests/tests/vueNodes/widgets/load/uploadWidgets.spec.ts-snapshots/vue-nodes-upload-widgets-chromium-linux.png b/browser_tests/tests/vueNodes/widgets/load/uploadWidgets.spec.ts-snapshots/vue-nodes-upload-widgets-chromium-linux.png index 7bc3a0a66a..092210ea3f 100644 Binary files a/browser_tests/tests/vueNodes/widgets/load/uploadWidgets.spec.ts-snapshots/vue-nodes-upload-widgets-chromium-linux.png and b/browser_tests/tests/vueNodes/widgets/load/uploadWidgets.spec.ts-snapshots/vue-nodes-upload-widgets-chromium-linux.png differ diff --git a/src/components/TopMenuSection.vue b/src/components/TopMenuSection.vue index 9714b81534..d7d4a08da3 100644 --- a/src/components/TopMenuSection.vue +++ b/src/components/TopMenuSection.vue @@ -1,78 +1,56 @@