Fix uploaded image not forcing re-render (#3115)

Co-authored-by: github-actions <github-actions@github.com>
This commit is contained in:
Christian Byrne
2025-03-17 17:13:05 -07:00
committed by GitHub
parent da415e9d80
commit 26a7ebdd77
6 changed files with 105 additions and 10 deletions

View File

@@ -459,7 +459,14 @@ export class ComfyPage {
await this.nextFrame() await this.nextFrame()
} }
async dragAndDropFile(fileName: string) { async dragAndDropFile(
fileName: string,
options: {
dropPosition?: Position
} = {}
) {
const { dropPosition = { x: 100, y: 100 } } = options
const filePath = this.assetPath(fileName) const filePath = this.assetPath(fileName)
// Read the file content // Read the file content
@@ -477,32 +484,56 @@ export class ComfyPage {
const fileType = getFileType(fileName) const fileType = getFileType(fileName)
await this.page.evaluate( await this.page.evaluate(
async ({ buffer, fileName, fileType }) => { async ({ buffer, fileName, fileType, dropPosition }) => {
const file = new File([new Uint8Array(buffer)], fileName, { const file = new File([new Uint8Array(buffer)], fileName, {
type: fileType type: fileType
}) })
const dataTransfer = new DataTransfer() const dataTransfer = new DataTransfer()
dataTransfer.items.add(file) dataTransfer.items.add(file)
const dropEvent = new DragEvent('drop', { const targetElement = document.elementFromPoint(
dropPosition.x,
dropPosition.y
)
if (!targetElement) {
console.error('No element found at drop position:', dropPosition)
return { success: false, error: 'No element at position' }
}
const eventOptions = {
bubbles: true, bubbles: true,
cancelable: true, cancelable: true,
dataTransfer dataTransfer,
}) clientX: dropPosition.x,
clientY: dropPosition.y
}
const dragOverEvent = new DragEvent('dragover', eventOptions)
const dropEvent = new DragEvent('drop', eventOptions)
Object.defineProperty(dropEvent, 'preventDefault', { Object.defineProperty(dropEvent, 'preventDefault', {
value: () => {}, value: () => {},
writable: false writable: false
}) })
Object.defineProperty(dropEvent, 'stopPropagation', { Object.defineProperty(dropEvent, 'stopPropagation', {
value: () => {}, value: () => {},
writable: false writable: false
}) })
document.dispatchEvent(dropEvent) targetElement.dispatchEvent(dragOverEvent)
targetElement.dispatchEvent(dropEvent)
return {
success: true,
targetInfo: {
tagName: targetElement.tagName,
id: targetElement.id,
classList: Array.from(targetElement.classList)
}
}
}, },
{ buffer: [...new Uint8Array(buffer)], fileName, fileType } { buffer: [...new Uint8Array(buffer)], fileName, fileType, dropPosition }
) )
await this.nextFrame() await this.nextFrame()

View File

@@ -115,8 +115,20 @@ export class NodeWidgetReference {
} }
) )
} }
}
async getValue() {
return await this.node.comfyPage.page.evaluate(
([id, index]) => {
const node = window['app'].graph.getNodeById(id)
if (!node) throw new Error(`Node ${id} not found.`)
const widget = node.widgets[index]
if (!widget) throw new Error(`Widget ${index} not found.`)
return widget.value
},
[this.node.id, this.index] as const
)
}
}
export class NodeReference { export class NodeReference {
constructor( constructor(
readonly id: NodeId, readonly id: NodeId,

View File

@@ -128,11 +128,62 @@ test.describe('Dynamic widget manipulation', () => {
}) })
}) })
test.describe('Load image widget', () => { test.describe('Image widget', () => {
test('Can load image', async ({ comfyPage }) => { test('Can load image', async ({ comfyPage }) => {
await comfyPage.loadWorkflow('widgets/load_image_widget') await comfyPage.loadWorkflow('widgets/load_image_widget')
await expect(comfyPage.canvas).toHaveScreenshot('load_image_widget.png') await expect(comfyPage.canvas).toHaveScreenshot('load_image_widget.png')
}) })
test('Can drag and drop image', async ({ comfyPage }) => {
await comfyPage.loadWorkflow('widgets/load_image_widget')
// Get position of the load image node
const nodes = await comfyPage.getNodeRefsByType('LoadImage')
const loadImageNode = nodes[0]
const { x, y } = await loadImageNode.getPosition()
// Drag and drop image file onto the load image node
await comfyPage.dragAndDropFile('image32x32.webp', {
dropPosition: { x, y }
})
// Expect the image preview to change automatically
await expect(comfyPage.canvas).toHaveScreenshot(
'image_preview_drag_and_dropped.png'
)
// Expect the filename combo value to be updated
const fileComboWidget = await loadImageNode.getWidget(0)
const filename = await fileComboWidget.getValue()
expect(filename).toBe('image32x32.webp')
})
test('Can change image by changing the filename combo value', async ({
comfyPage
}) => {
await comfyPage.loadWorkflow('widgets/load_image_widget')
const nodes = await comfyPage.getNodeRefsByType('LoadImage')
const loadImageNode = nodes[0]
// Click the combo widget used to select the image filename
const fileComboWidget = await loadImageNode.getWidget(0)
await fileComboWidget.click()
// Select a new image filename value from the combo context menu
const comboEntry = comfyPage.page.getByRole('menuitem', {
name: 'image32x32.webp'
})
await comboEntry.click({ noWaitAfter: true })
// Expect the image preview to change automatically
await expect(comfyPage.canvas).toHaveScreenshot(
'image_preview_changed_by_combo_value.png'
)
// Expect the filename combo value to be updated
const filename = await fileComboWidget.getValue()
expect(filename).toBe('image32x32.webp')
})
}) })
test.describe('Load audio widget', () => { test.describe('Load audio widget', () => {

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

View File

@@ -93,6 +93,7 @@ export const useImageUploadWidget = () => {
// Add our own callback to the combo widget to render an image when it changes // Add our own callback to the combo widget to render an image when it changes
fileComboWidget.callback = function () { fileComboWidget.callback = function () {
nodeOutputStore.setNodeOutputs(node, fileComboWidget.value) nodeOutputStore.setNodeOutputs(node, fileComboWidget.value)
node.graph?.setDirtyCanvas(true)
} }
// On load if we have a value then render the image // On load if we have a value then render the image