mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-30 03:01:54 +00:00
Amp-Thread-ID: https://ampcode.com/threads/T-019c1640-f128-732b-b621-2621ce8b48d1 Co-authored-by: Amp <amp@ampcode.com>
4.7 KiB
4.7 KiB
Advanced Patterns
Change Transactions (Grouped Undo)
Group multiple changes for single undo:
await comfyPage.page.evaluate(() => {
window['app'].canvas.emitBeforeChange()
})
// ... make multiple changes ...
await comfyPage.page.evaluate(() => {
window['app'].canvas.emitAfterChange()
})
Mark Canvas Dirty
After programmatic changes to the graph:
await comfyPage.page.evaluate(() => {
window['app'].graph.nodes[0].addWidget('number', 'new_widget', 10)
window['app'].graph.setDirtyCanvas(true, true) // Required!
})
await comfyPage.nextFrame()
Wait for Workflow Service
After file operations, wait for workflow service to settle:
await comfyPage.page.waitForFunction(
() => window['app']?.extensionManager?.workflow?.isBusy === false,
undefined,
{ timeout: 3000 }
)
Dialog Mask Handling
Wait for dialog masks to disappear:
const mask = comfyPage.page.locator('.p-dialog-mask')
if ((await mask.count()) > 0) {
await mask.first().waitFor({ state: 'hidden', timeout: 3000 })
}
LiteGraph Context Menu
Click items in LiteGraph context menus:
await node.click('title', { button: 'right' })
await comfyPage.clickLitegraphContextMenuItem('Remove Slot')
LiteGraph Prompt Dialog
Handle LiteGraph's prompt dialogs:
await comfyPage.page.waitForSelector('.graphdialog input', { state: 'visible' })
await comfyPage.page.fill('.graphdialog input', 'new_name')
await comfyPage.page.keyboard.press('Enter')
Move Mouse Away for Clean Screenshots
Avoid hover effects in screenshots:
await node.click('title')
await comfyPage.moveMouseToEmptyArea()
await expect(comfyPage.canvas).toHaveScreenshot('clean.png')
Setup Workflows Directory
Set up/clear workflows for a test:
test.afterEach(async ({ comfyPage }) => {
await comfyPage.setupWorkflowsDirectory({}) // Clear all
})
// Or set up specific workflows
await comfyPage.setupWorkflowsDirectory({
'workflow1.json': workflowData1,
'workflow2.json': workflowData2
})
Execute Commands
Run ComfyUI commands:
await comfyPage.executeCommand('Comfy.LoadDefaultWorkflow')
await comfyPage.executeCommand('Comfy.NewBlankWorkflow')
await comfyPage.executeCommand('Comfy.ClearWorkflow')
Access App Instance
Direct access to the app instance:
const result = await comfyPage.page.evaluate(() => {
return window['app'].graph.nodes.length
})
WebSocket Mocking
Mock WebSocket status updates:
// Get WebSocket fixture
const { websocket } = await comfyPage.getWebSocket()
// Send mock status
await websocket.send(
JSON.stringify({
type: 'status',
data: { exec_info: { queue_remaining: 0 } }
})
)
Vue Node Testing
Test Vue-rendered nodes:
const vueNode = comfyPage.vueNodes.getByTitle('My Vue Node')
await vueNode.waitForRender()
const content = await vueNode.getContent()
Test with Ctrl Modifier
Click with modifier keys:
await node.click('title', { modifiers: ['Control'] })
await node.click('title', { modifiers: ['Shift'] })
await node.click('title', { modifiers: ['Control', 'Shift'] })
Parallel Test Isolation
Each test gets unique user ID for isolation:
test('isolated test', async ({ comfyPage }) => {
// comfyPage.id is unique per test instance
const uniqueWorkflow = `workflow-${comfyPage.id}.json`
})
Platform-Specific Tests
Skip tests based on platform:
test('linux only', async ({ comfyPage }) => {
test.skip(process.platform !== 'linux', 'Linux only test')
// ...
})
Wait for Animation
Wait for CSS animations to complete:
await comfyPage.page.waitForFunction(() => {
const el = document.querySelector('.animated-element')
return (
getComputedStyle(el).animationPlayState === 'paused' ||
getComputedStyle(el).animationName === 'none'
)
})
Capture Network Response
Wait for and capture specific network responses:
const responsePromise = comfyPage.page.waitForResponse('**/api/history')
await comfyPage.page.click('[data-testid="history-btn"]')
const response = await responsePromise
const data = await response.json()
Retry Flaky Operations
Use toPass for inherently flaky assertions:
await expect(async () => {
const value = await widget.getValue()
expect(value).toBe(100)
}).toPass({ timeout: 2000, intervals: [100, 200, 500] })
Screenshot with Masking
Mask dynamic content in screenshots:
await expect(comfyPage.canvas).toHaveScreenshot('page.png', {
mask: [
comfyPage.page.locator('.timestamp'),
comfyPage.page.locator('.random-id')
]
})