diff --git a/browser_tests/fixtures/ComfyPage.ts b/browser_tests/fixtures/ComfyPage.ts index 52f03629f..ed1409425 100644 --- a/browser_tests/fixtures/ComfyPage.ts +++ b/browser_tests/fixtures/ComfyPage.ts @@ -459,7 +459,14 @@ export class ComfyPage { 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) // Read the file content @@ -477,32 +484,56 @@ export class ComfyPage { const fileType = getFileType(fileName) await this.page.evaluate( - async ({ buffer, fileName, fileType }) => { + async ({ buffer, fileName, fileType, dropPosition }) => { const file = new File([new Uint8Array(buffer)], fileName, { type: fileType }) const dataTransfer = new DataTransfer() 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, 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', { value: () => {}, writable: false }) - Object.defineProperty(dropEvent, 'stopPropagation', { value: () => {}, 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() diff --git a/browser_tests/fixtures/utils/litegraphUtils.ts b/browser_tests/fixtures/utils/litegraphUtils.ts index e62a7c685..1f102bbb5 100644 --- a/browser_tests/fixtures/utils/litegraphUtils.ts +++ b/browser_tests/fixtures/utils/litegraphUtils.ts @@ -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 { constructor( readonly id: NodeId, diff --git a/browser_tests/tests/widget.spec.ts b/browser_tests/tests/widget.spec.ts index 3ea5255a5..0e6045bc2 100644 --- a/browser_tests/tests/widget.spec.ts +++ b/browser_tests/tests/widget.spec.ts @@ -128,11 +128,62 @@ test.describe('Dynamic widget manipulation', () => { }) }) -test.describe('Load image widget', () => { +test.describe('Image widget', () => { test('Can load image', async ({ comfyPage }) => { await comfyPage.loadWorkflow('widgets/load_image_widget') 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', () => { diff --git a/browser_tests/tests/widget.spec.ts-snapshots/image-preview-changed-by-combo-value-chromium-linux.png b/browser_tests/tests/widget.spec.ts-snapshots/image-preview-changed-by-combo-value-chromium-linux.png new file mode 100644 index 000000000..fc573edf4 Binary files /dev/null and b/browser_tests/tests/widget.spec.ts-snapshots/image-preview-changed-by-combo-value-chromium-linux.png differ diff --git a/browser_tests/tests/widget.spec.ts-snapshots/image-preview-drag-and-dropped-chromium-linux.png b/browser_tests/tests/widget.spec.ts-snapshots/image-preview-drag-and-dropped-chromium-linux.png new file mode 100644 index 000000000..fc573edf4 Binary files /dev/null and b/browser_tests/tests/widget.spec.ts-snapshots/image-preview-drag-and-dropped-chromium-linux.png differ diff --git a/src/composables/widgets/useImageUploadWidget.ts b/src/composables/widgets/useImageUploadWidget.ts index d31e74d3b..a8e93fa9c 100644 --- a/src/composables/widgets/useImageUploadWidget.ts +++ b/src/composables/widgets/useImageUploadWidget.ts @@ -93,6 +93,7 @@ export const useImageUploadWidget = () => { // Add our own callback to the combo widget to render an image when it changes fileComboWidget.callback = function () { nodeOutputStore.setNodeOutputs(node, fileComboWidget.value) + node.graph?.setDirtyCanvas(true) } // On load if we have a value then render the image