mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-05-03 04:31:58 +00:00
Add Playwright tests for async execution
This commit is contained in:
316
browser_tests/tests/browserTabTitleMultiNode.spec.ts
Normal file
316
browser_tests/tests/browserTabTitleMultiNode.spec.ts
Normal file
@@ -0,0 +1,316 @@
|
||||
import { expect, mergeTests } from '@playwright/test'
|
||||
|
||||
import { comfyPageFixture } from '../fixtures/ComfyPage'
|
||||
import { webSocketFixture } from '../fixtures/ws'
|
||||
import {
|
||||
BrowserTitleMonitor,
|
||||
ExecutionTestHelper
|
||||
} from '../helpers/ExecutionTestHelper'
|
||||
|
||||
const test = mergeTests(comfyPageFixture, webSocketFixture)
|
||||
|
||||
test.describe('Browser Tab Title - Multi-node Execution', () => {
|
||||
test.describe.configure({ mode: 'serial' })
|
||||
|
||||
let executionHelper: ExecutionTestHelper
|
||||
let titleMonitor: BrowserTitleMonitor
|
||||
|
||||
test.beforeEach(async ({ comfyPage }) => {
|
||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Top')
|
||||
executionHelper = new ExecutionTestHelper(comfyPage.page)
|
||||
titleMonitor = new BrowserTitleMonitor(comfyPage.page)
|
||||
})
|
||||
|
||||
test.afterEach(async () => {
|
||||
// Clean up event listeners to avoid conflicts
|
||||
if (executionHelper) {
|
||||
await executionHelper.cleanup()
|
||||
}
|
||||
})
|
||||
|
||||
test('Shows multiple nodes running in tab title', async ({
|
||||
comfyPage,
|
||||
ws
|
||||
}) => {
|
||||
await comfyPage.loadWorkflow('execution/parallel_async_nodes')
|
||||
|
||||
// Wait for any existing execution to complete
|
||||
await titleMonitor.waitForIdleTitle()
|
||||
|
||||
// Get initial title
|
||||
const initialTitle = await comfyPage.page.title()
|
||||
// Title might show execution state if other tests are running
|
||||
// Just ensure we have a baseline to compare against
|
||||
|
||||
// Wait for the UI to be ready
|
||||
await comfyPage.nextFrame()
|
||||
|
||||
// Check if workflow is valid and nodes are available
|
||||
const workflowStatus = await comfyPage.page.evaluate(() => {
|
||||
const graph = window['app'].graph
|
||||
const missingNodeTypes: string[] = []
|
||||
const nodeCount = graph.nodes.length
|
||||
|
||||
// Check for missing node types
|
||||
graph.nodes.forEach((node: any) => {
|
||||
if (node.type && !LiteGraph.registered_node_types[node.type]) {
|
||||
missingNodeTypes.push(node.type)
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
nodeCount,
|
||||
missingNodeTypes,
|
||||
hasErrors: missingNodeTypes.length > 0
|
||||
}
|
||||
})
|
||||
|
||||
if (workflowStatus.hasErrors) {
|
||||
console.log('Missing node types:', workflowStatus.missingNodeTypes)
|
||||
// Skip test if nodes are missing
|
||||
test.skip()
|
||||
return
|
||||
}
|
||||
|
||||
// Set up tracking for progress events and errors
|
||||
await executionHelper.setupEventTracking()
|
||||
|
||||
// Queue the workflow for real execution using the command
|
||||
await comfyPage.executeCommand('Comfy.QueuePrompt')
|
||||
|
||||
// Wait a moment to see if there's an error
|
||||
await comfyPage.page.waitForTimeout(1000)
|
||||
|
||||
// Check for execution errors
|
||||
if (await executionHelper.hasExecutionError()) {
|
||||
const error = await executionHelper.getExecutionError()
|
||||
console.log('Execution error:', error)
|
||||
}
|
||||
|
||||
// Wait for multiple nodes to be running (TestSleep nodes 2, 3 and TestAsyncProgressNode 4)
|
||||
await executionHelper.waitForRunningNodes(2)
|
||||
|
||||
// Check title while we know multiple nodes are running
|
||||
const testId = executionHelper.getTestId()
|
||||
const titleDuringExecution = await comfyPage.page.evaluate((testId) => {
|
||||
const states = window[`__progressStates_${testId}`]
|
||||
if (!states || states.length === 0) return null
|
||||
|
||||
const latestState = states[states.length - 1]
|
||||
if (!latestState.nodes) return null
|
||||
|
||||
const runningNodes = Object.values(latestState.nodes).filter(
|
||||
(node: any) => node.state === 'running'
|
||||
).length
|
||||
|
||||
return {
|
||||
title: document.title,
|
||||
runningCount: runningNodes
|
||||
}
|
||||
}, testId)
|
||||
|
||||
// Verify we captured the state with multiple nodes running
|
||||
expect(titleDuringExecution).not.toBeNull()
|
||||
expect(titleDuringExecution.runningCount).toBeGreaterThanOrEqual(2)
|
||||
|
||||
// The title should show multiple nodes running when we have 2+ nodes executing
|
||||
if (titleDuringExecution.runningCount >= 2) {
|
||||
expect(titleDuringExecution.title).toMatch(/\[\d+ nodes running\]/)
|
||||
}
|
||||
|
||||
// Wait for some nodes to finish, leaving only one running
|
||||
await executionHelper.waitForRunningNodes(1, 15000)
|
||||
|
||||
// Wait for title to show single node progress
|
||||
await comfyPage.page.waitForFunction(
|
||||
() => {
|
||||
const title = document.title
|
||||
return title.match(/\[\d+%\]/) && !title.match(/\[\d+ nodes running\]/)
|
||||
},
|
||||
{ timeout: 5000 }
|
||||
)
|
||||
|
||||
// Check that title shows single node with progress
|
||||
const titleWithSingleNode = await comfyPage.page.title()
|
||||
expect(titleWithSingleNode).toMatch(/\[\d+%\]/)
|
||||
expect(titleWithSingleNode).not.toMatch(/\[\d+ nodes running\]/)
|
||||
})
|
||||
|
||||
test('Shows progress updates in title during execution', async ({
|
||||
comfyPage,
|
||||
ws
|
||||
}) => {
|
||||
await comfyPage.loadWorkflow('execution/parallel_async_nodes')
|
||||
|
||||
// Wait for the UI to be ready
|
||||
await comfyPage.nextFrame()
|
||||
|
||||
// Set up tracking for progress events and title changes
|
||||
await executionHelper.setupEventTracking()
|
||||
await titleMonitor.setupTitleMonitoring()
|
||||
|
||||
// Queue the workflow for real execution using the command
|
||||
await comfyPage.executeCommand('Comfy.QueuePrompt')
|
||||
|
||||
// Wait for TestAsyncProgressNode (node 4) to start showing progress
|
||||
// This node reports progress from 0 to 10 with steps of 1
|
||||
const testId2 = executionHelper.getTestId()
|
||||
await comfyPage.page.waitForFunction(
|
||||
(testId) => {
|
||||
const states = window[`__progressStates_${testId}`]
|
||||
if (!states || states.length === 0) return false
|
||||
|
||||
const latestState = states[states.length - 1]
|
||||
if (!latestState.nodes || !latestState.nodes['4']) return false
|
||||
|
||||
const node4 = latestState.nodes['4']
|
||||
if (node4.state === 'running' && node4.value > 0) {
|
||||
window['__lastProgress'] = Math.round((node4.value / node4.max) * 100)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
},
|
||||
testId2,
|
||||
{ timeout: 10000 }
|
||||
)
|
||||
|
||||
// Wait for title to show progress percentage
|
||||
await comfyPage.page.waitForFunction(
|
||||
() => {
|
||||
const title = document.title
|
||||
console.log('Title check 1:', title)
|
||||
return title.match(/\[\d+%\]/)
|
||||
},
|
||||
{ timeout: 5000 }
|
||||
)
|
||||
|
||||
// Check that title shows a progress percentage
|
||||
const titleWithProgress = await comfyPage.page.title()
|
||||
expect(titleWithProgress).toMatch(/\[\d+%\]/)
|
||||
|
||||
// Wait for progress to update to a different value
|
||||
const firstProgress = await comfyPage.page.evaluate(
|
||||
() => window['__lastProgress']
|
||||
)
|
||||
|
||||
const testId3 = executionHelper.getTestId()
|
||||
await comfyPage.page.waitForFunction(
|
||||
({ initialProgress, testId }) => {
|
||||
const states = window[`__progressStates_${testId}`]
|
||||
if (!states || states.length === 0) return false
|
||||
|
||||
const latestState = states[states.length - 1]
|
||||
if (!latestState.nodes || !latestState.nodes['4']) return false
|
||||
|
||||
const node4 = latestState.nodes['4']
|
||||
if (node4.state === 'running') {
|
||||
const currentProgress = Math.round((node4.value / node4.max) * 100)
|
||||
window['__lastProgress'] = currentProgress
|
||||
return currentProgress > initialProgress
|
||||
}
|
||||
return false
|
||||
},
|
||||
{ initialProgress: firstProgress, testId: testId3 },
|
||||
{ timeout: 10000 }
|
||||
)
|
||||
|
||||
// Store the first progress for comparison
|
||||
await comfyPage.page.evaluate((progress) => {
|
||||
window['__firstProgress'] = progress
|
||||
}, firstProgress)
|
||||
|
||||
// Check the title history to verify we captured progress updates
|
||||
const finalCheck = await comfyPage.page.evaluate(() => {
|
||||
const titleLog = window['__titleUpdateLog'] || []
|
||||
const firstProgress = window['__firstProgress'] || 0
|
||||
|
||||
// Find titles with progress information
|
||||
const titlesWithProgress = titleLog.filter((entry) => entry.hasProgress)
|
||||
|
||||
// Check if we saw different progress values or multi-node running state
|
||||
const progressValues = new Set()
|
||||
const hadMultiNodeRunning = titleLog.some((entry) =>
|
||||
entry.title.includes('nodes running')
|
||||
)
|
||||
|
||||
titleLog.forEach((entry) => {
|
||||
const match = entry.title.match(/\[(\d+)%\]/)
|
||||
if (match) {
|
||||
progressValues.add(parseInt(match[1]))
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
sawProgressUpdates: titlesWithProgress.length > 0,
|
||||
uniqueProgressValues: Array.from(progressValues),
|
||||
hadMultiNodeRunning,
|
||||
firstProgress,
|
||||
lastProgress: window['__lastProgress'],
|
||||
totalTitleUpdates: titleLog.length,
|
||||
sampleTitles: titleLog.slice(0, 5)
|
||||
}
|
||||
})
|
||||
|
||||
console.log('Title update check:', JSON.stringify(finalCheck, null, 2))
|
||||
|
||||
// Verify that we captured title updates showing execution progress
|
||||
expect(finalCheck.sawProgressUpdates).toBe(true)
|
||||
expect(finalCheck.totalTitleUpdates).toBeGreaterThan(0)
|
||||
|
||||
// We should have seen either:
|
||||
// 1. Multiple unique progress values, OR
|
||||
// 2. Multi-node running state, OR
|
||||
// 3. Progress different from initial
|
||||
const sawProgressChange =
|
||||
finalCheck.uniqueProgressValues.length > 1 ||
|
||||
finalCheck.hadMultiNodeRunning ||
|
||||
finalCheck.lastProgress !== firstProgress
|
||||
|
||||
expect(sawProgressChange).toBe(true)
|
||||
|
||||
// Clean up interval
|
||||
await titleMonitor.stopTitleMonitoring()
|
||||
})
|
||||
|
||||
test('Clears execution status from title when all nodes finish', async ({
|
||||
comfyPage,
|
||||
ws
|
||||
}) => {
|
||||
await comfyPage.loadWorkflow('execution/parallel_async_nodes')
|
||||
|
||||
// Wait for any existing execution to complete
|
||||
await titleMonitor.waitForIdleTitle()
|
||||
|
||||
// Wait for the UI to be ready
|
||||
await comfyPage.nextFrame()
|
||||
|
||||
// Set up tracking for events
|
||||
await executionHelper.setupEventTracking()
|
||||
|
||||
// Queue the workflow for real execution using the command
|
||||
await comfyPage.executeCommand('Comfy.QueuePrompt')
|
||||
|
||||
// Wait for execution to show progress in title
|
||||
await titleMonitor.waitForExecutionTitle()
|
||||
|
||||
// Verify execution shows in title
|
||||
const executingTitle = await comfyPage.page.title()
|
||||
expect(executingTitle).toMatch(/\[[\d%\s\w]+\]/)
|
||||
|
||||
// Wait for execution to complete (all nodes finished)
|
||||
await executionHelper.waitForExecutionFinish()
|
||||
|
||||
// Give a moment for title to update after execution completes
|
||||
await comfyPage.page.waitForTimeout(500)
|
||||
|
||||
// Wait for title to clear execution status
|
||||
await titleMonitor.waitForIdleTitle()
|
||||
|
||||
// Check that execution status is cleared
|
||||
const finishedTitle = await comfyPage.page.title()
|
||||
expect(finishedTitle).toContain('ComfyUI')
|
||||
expect(finishedTitle).not.toMatch(/\[\d+%\]/) // No percentage
|
||||
expect(finishedTitle).not.toMatch(/\[\d+ nodes running\]/) // No running nodes
|
||||
expect(finishedTitle).not.toContain('Executing')
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user