mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-05 15:40:10 +00:00
273 lines
8.4 KiB
TypeScript
273 lines
8.4 KiB
TypeScript
import { expect, mergeTests } from '@playwright/test'
|
|
|
|
import { comfyPageFixture } from '../fixtures/ComfyPage'
|
|
import { webSocketFixture } from '../fixtures/ws'
|
|
import {
|
|
ExecutionTestHelper,
|
|
PreviewTestHelper
|
|
} from '../helpers/ExecutionTestHelper'
|
|
|
|
const test = mergeTests(comfyPageFixture, webSocketFixture)
|
|
|
|
test.describe('Preview with Metadata', () => {
|
|
test.describe.configure({ mode: 'serial' })
|
|
|
|
let executionHelper: ExecutionTestHelper
|
|
let previewHelper: PreviewTestHelper
|
|
|
|
test.beforeEach(async ({ comfyPage }) => {
|
|
executionHelper = new ExecutionTestHelper(comfyPage.page)
|
|
previewHelper = new PreviewTestHelper(comfyPage.page)
|
|
})
|
|
|
|
test.afterEach(async () => {
|
|
if (executionHelper) {
|
|
await executionHelper.cleanup()
|
|
}
|
|
})
|
|
|
|
test('Handles b_preview_with_metadata event correctly', async ({
|
|
comfyPage,
|
|
ws
|
|
}) => {
|
|
await comfyPage.loadWorkflow('execution/parallel_async_nodes')
|
|
|
|
// Clear any existing previews
|
|
await comfyPage.page.evaluate(() => {
|
|
window['app'].nodePreviewImages = {}
|
|
})
|
|
|
|
// Set up handler to track preview events and execution
|
|
await executionHelper.setupEventTracking()
|
|
await comfyPage.page.evaluate(() => {
|
|
window['__previewHandled'] = false
|
|
const api = window['app'].api
|
|
|
|
// Add handler to track preview events
|
|
api.addEventListener('b_preview_with_metadata', (event) => {
|
|
const { displayNodeId, blob } = event.detail
|
|
// Create URL from the blob in the event
|
|
const url = URL.createObjectURL(blob)
|
|
window['app'].nodePreviewImages[displayNodeId] = [url]
|
|
window['__previewHandled'] = true
|
|
window['__lastPreviewUrl'] = url
|
|
})
|
|
})
|
|
|
|
// Start real execution to test event handling in context
|
|
await comfyPage.executeCommand('Comfy.QueuePrompt')
|
|
|
|
// Wait for execution to start
|
|
await executionHelper.waitForExecutionStart()
|
|
|
|
// Trigger b_preview_with_metadata event (simulating what backend would send)
|
|
await comfyPage.page.evaluate(() => {
|
|
const api = window['app'].api
|
|
const event = new CustomEvent('b_preview_with_metadata', {
|
|
detail: {
|
|
blob: new Blob(['test'], { type: 'image/png' }),
|
|
nodeId: '2',
|
|
displayNodeId: '2',
|
|
parentNodeId: '2',
|
|
realNodeId: '2',
|
|
promptId: 'test-prompt-id'
|
|
}
|
|
})
|
|
api.dispatchEvent(event)
|
|
})
|
|
|
|
await comfyPage.nextFrame()
|
|
|
|
// Wait for preview to be handled
|
|
await comfyPage.page.waitForFunction(
|
|
() => window['__previewHandled'] === true,
|
|
{ timeout: 5000 }
|
|
)
|
|
|
|
// Check that preview was set for the correct node
|
|
const result = await comfyPage.page.evaluate(() => {
|
|
return {
|
|
previewImages: window['app'].nodePreviewImages,
|
|
lastUrl: window['__lastPreviewUrl']
|
|
}
|
|
})
|
|
|
|
expect(result.previewImages['2']).toBeDefined()
|
|
expect(result.previewImages['2']).toHaveLength(1)
|
|
expect(result.previewImages['2'][0]).toBe(result.lastUrl)
|
|
})
|
|
|
|
test('Clears old previews when new preview arrives', async ({
|
|
comfyPage,
|
|
ws
|
|
}) => {
|
|
await comfyPage.loadWorkflow('execution/parallel_async_nodes')
|
|
|
|
// Set up initial preview
|
|
const initialBlobUrl = await comfyPage.page.evaluate(() => {
|
|
const blob = new Blob(['initial image'], { type: 'image/png' })
|
|
const url = URL.createObjectURL(blob)
|
|
window['app'].nodePreviewImages['3'] = [url]
|
|
return url
|
|
})
|
|
|
|
// Create spy to track URL revocations
|
|
await previewHelper.setupPreviewTracking()
|
|
|
|
// Mock the handler to revoke old previews
|
|
await comfyPage.page.evaluate(() => {
|
|
const api = window['app'].api
|
|
api.addEventListener('b_preview_with_metadata', (event) => {
|
|
const { displayNodeId } = event.detail
|
|
window['app'].revokePreviews(displayNodeId)
|
|
const newBlob = new Blob(['new image'], { type: 'image/png' })
|
|
const newUrl = URL.createObjectURL(newBlob)
|
|
window['app'].nodePreviewImages[displayNodeId] = [newUrl]
|
|
})
|
|
})
|
|
|
|
// Trigger new preview for same node
|
|
await comfyPage.page.evaluate(() => {
|
|
const api = window['app'].api
|
|
const event = new CustomEvent('b_preview_with_metadata', {
|
|
detail: {
|
|
blob: new Blob(['new image'], { type: 'image/png' }),
|
|
nodeId: '3',
|
|
displayNodeId: '3',
|
|
parentNodeId: '3',
|
|
realNodeId: '3',
|
|
promptId: 'test-prompt-id'
|
|
}
|
|
})
|
|
api.dispatchEvent(event)
|
|
})
|
|
|
|
await comfyPage.nextFrame()
|
|
|
|
// Check that old URL was revoked
|
|
const finalRevokedUrls = await previewHelper.getRevokedUrls()
|
|
expect(finalRevokedUrls).toContain(initialBlobUrl)
|
|
|
|
// Check that new preview replaced old one
|
|
const newPreviewImages = await previewHelper.getNodePreviews('3')
|
|
|
|
expect(newPreviewImages).toHaveLength(1)
|
|
expect(newPreviewImages[0]).not.toBe(initialBlobUrl)
|
|
})
|
|
|
|
test('Associates preview with correct display node in subgraph', async ({
|
|
comfyPage,
|
|
ws
|
|
}) => {
|
|
await comfyPage.loadWorkflow('execution/parallel_async_nodes')
|
|
|
|
// Mock handler that stores metadata
|
|
await previewHelper.setupPreviewTracking()
|
|
await comfyPage.page.evaluate(() => {
|
|
window['__previewMetadata'] = {}
|
|
const api = window['app'].api
|
|
api.addEventListener('b_preview_with_metadata', (event) => {
|
|
const { displayNodeId, nodeId, parentNodeId, realNodeId, promptId } =
|
|
event.detail
|
|
window['__previewMetadata'][displayNodeId] = {
|
|
nodeId,
|
|
displayNodeId,
|
|
parentNodeId,
|
|
realNodeId,
|
|
promptId
|
|
}
|
|
// Still create the preview
|
|
const url = URL.createObjectURL(event.detail.blob)
|
|
window['app'].nodePreviewImages[displayNodeId] = [url]
|
|
})
|
|
})
|
|
|
|
// Simulate preview from a subgraph node
|
|
await comfyPage.page.evaluate(() => {
|
|
const api = window['app'].api
|
|
const event = new CustomEvent('b_preview_with_metadata', {
|
|
detail: {
|
|
blob: new Blob(['subgraph preview'], { type: 'image/png' }),
|
|
nodeId: '10:5:3', // Nested execution ID
|
|
displayNodeId: '10', // Top-level display node
|
|
parentNodeId: '10:5', // Parent subgraph
|
|
realNodeId: '3', // Actual node ID within subgraph
|
|
promptId: 'test-prompt-id'
|
|
}
|
|
})
|
|
api.dispatchEvent(event)
|
|
})
|
|
|
|
await comfyPage.nextFrame()
|
|
|
|
// Check that preview is associated with display node
|
|
const metadata = await comfyPage.page.evaluate(
|
|
() => window['__previewMetadata']
|
|
)
|
|
expect(metadata['10']).toBeDefined()
|
|
expect(metadata['10'].nodeId).toBe('10:5:3')
|
|
expect(metadata['10'].displayNodeId).toBe('10')
|
|
expect(metadata['10'].parentNodeId).toBe('10:5')
|
|
expect(metadata['10'].realNodeId).toBe('3')
|
|
|
|
// Check that preview exists for display node
|
|
const previews = await comfyPage.page.evaluate(
|
|
() => window['app'].nodePreviewImages
|
|
)
|
|
expect(previews['10']).toBeDefined()
|
|
expect(previews['10']).toHaveLength(1)
|
|
})
|
|
|
|
test('Maintains backward compatibility with b_preview event', async ({
|
|
comfyPage,
|
|
ws
|
|
}) => {
|
|
await comfyPage.loadWorkflow('execution/parallel_async_nodes')
|
|
|
|
// Track both events
|
|
const eventsFired = await comfyPage.page.evaluate(() => {
|
|
const events: string[] = []
|
|
const api = window['app'].api
|
|
|
|
api.addEventListener('b_preview', () => {
|
|
events.push('b_preview')
|
|
})
|
|
|
|
api.addEventListener('b_preview_with_metadata', () => {
|
|
events.push('b_preview_with_metadata')
|
|
})
|
|
|
|
window['__eventsFired'] = events
|
|
return events
|
|
})
|
|
|
|
// Trigger b_preview_with_metadata
|
|
await comfyPage.page.evaluate(() => {
|
|
const api = window['app'].api
|
|
const blob = new Blob(['test image'], { type: 'image/png' })
|
|
|
|
// Simulate the API behavior
|
|
api.dispatchCustomEvent('b_preview_with_metadata', {
|
|
blob,
|
|
nodeId: '2',
|
|
displayNodeId: '2',
|
|
parentNodeId: '2',
|
|
realNodeId: '2',
|
|
promptId: 'test-prompt-id'
|
|
})
|
|
|
|
// Also dispatch legacy event as the API would
|
|
api.dispatchCustomEvent('b_preview', blob)
|
|
})
|
|
|
|
await comfyPage.nextFrame()
|
|
|
|
// Check that both events were fired
|
|
const finalEvents = await comfyPage.page.evaluate(
|
|
() => window['__eventsFired']
|
|
)
|
|
expect(finalEvents).toContain('b_preview_with_metadata')
|
|
expect(finalEvents).toContain('b_preview')
|
|
})
|
|
})
|