diff --git a/browser_tests/fixtures/ComfyPage.ts b/browser_tests/fixtures/ComfyPage.ts index b61a55fd26..b4ce59402f 100644 --- a/browser_tests/fixtures/ComfyPage.ts +++ b/browser_tests/fixtures/ComfyPage.ts @@ -463,87 +463,128 @@ export class ComfyPage { await this.nextFrame() } - async dragAndDropFile( - fileName: string, + async dragAndDropExternalResource( options: { + fileName?: string + url?: string dropPosition?: Position } = {} ) { - const { dropPosition = { x: 100, y: 100 } } = options + const { dropPosition = { x: 100, y: 100 }, fileName, url } = options - const filePath = this.assetPath(fileName) + if (!fileName && !url) + throw new Error('Must provide either fileName or url') - // Read the file content - const buffer = fs.readFileSync(filePath) + const evaluateParams: { + dropPosition: Position + fileName?: string + fileType?: string + buffer?: Uint8Array | number[] + url?: string + } = { dropPosition } - // Get file type - const getFileType = (fileName: string) => { - if (fileName.endsWith('.png')) return 'image/png' - if (fileName.endsWith('.webp')) return 'image/webp' - if (fileName.endsWith('.webm')) return 'video/webm' - if (fileName.endsWith('.json')) return 'application/json' - if (fileName.endsWith('.glb')) return 'model/gltf-binary' - return 'application/octet-stream' + // Dropping a file from the filesystem + if (fileName) { + const filePath = this.assetPath(fileName) + const buffer = fs.readFileSync(filePath) + + const getFileType = (fileName: string) => { + if (fileName.endsWith('.png')) return 'image/png' + if (fileName.endsWith('.webp')) return 'image/webp' + if (fileName.endsWith('.webm')) return 'video/webm' + if (fileName.endsWith('.json')) return 'application/json' + if (fileName.endsWith('.glb')) return 'model/gltf-binary' + return 'application/octet-stream' + } + + evaluateParams.fileName = fileName + evaluateParams.fileType = getFileType(fileName) + evaluateParams.buffer = [...new Uint8Array(buffer)] } - const fileType = getFileType(fileName) + // Dropping a URL (e.g., dropping image across browser tabs in Firefox) + if (url) evaluateParams.url = url - await this.page.evaluate( - async ({ buffer, fileName, fileType, dropPosition }) => { - const file = new File([new Uint8Array(buffer)], fileName, { - type: fileType - }) - const dataTransfer = new DataTransfer() - dataTransfer.items.add(file) + // Execute the drag and drop in the browser + await this.page.evaluate(async (params) => { + const dataTransfer = new DataTransfer() - 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, - 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 - }) - - targetElement.dispatchEvent(dragOverEvent) - targetElement.dispatchEvent(dropEvent) - - return { - success: true, - targetInfo: { - tagName: targetElement.tagName, - id: targetElement.id, - classList: Array.from(targetElement.classList) + // Add file if provided + if (params.buffer && params.fileName && params.fileType) { + const file = new File( + [new Uint8Array(params.buffer)], + params.fileName, + { + type: params.fileType } + ) + dataTransfer.items.add(file) + } + + // Add URL data if provided + if (params.url) { + dataTransfer.setData('text/uri-list', params.url) + dataTransfer.setData('text/x-moz-url', params.url) + } + + const targetElement = document.elementFromPoint( + params.dropPosition.x, + params.dropPosition.y + ) + + if (!targetElement) { + console.error('No element found at drop position:', params.dropPosition) + return { success: false, error: 'No element at position' } + } + + const eventOptions = { + bubbles: true, + cancelable: true, + dataTransfer, + clientX: params.dropPosition.x, + clientY: params.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 + }) + + 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, dropPosition } - ) + } + }, evaluateParams) await this.nextFrame() } + async dragAndDropFile( + fileName: string, + options: { dropPosition?: Position } = {} + ) { + return this.dragAndDropExternalResource({ fileName, ...options }) + } + + async dragAndDropURL(url: string, options: { dropPosition?: Position } = {}) { + return this.dragAndDropExternalResource({ url, ...options }) + } + async dragNode2() { await this.dragAndDrop({ x: 622, y: 400 }, { x: 622, y: 300 }) await this.nextFrame() diff --git a/browser_tests/tests/loadWorkflowInMedia.spec.ts b/browser_tests/tests/loadWorkflowInMedia.spec.ts index eb0c01e8bd..561ed74919 100644 --- a/browser_tests/tests/loadWorkflowInMedia.spec.ts +++ b/browser_tests/tests/loadWorkflowInMedia.spec.ts @@ -3,17 +3,35 @@ import { expect } from '@playwright/test' import { comfyPageFixture as test } from '../fixtures/ComfyPage' test.describe('Load Workflow in Media', () => { - ;[ + const fileNames = [ 'workflow.webp', 'edited_workflow.webp', 'no_workflow.webp', 'large_workflow.webp', 'workflow.webm', 'workflow.glb' - ].forEach(async (fileName) => { - test(`Load workflow in ${fileName}`, async ({ comfyPage }) => { + ] + fileNames.forEach(async (fileName) => { + test(`Load workflow in ${fileName} (drop from filesystem)`, async ({ + comfyPage + }) => { await comfyPage.dragAndDropFile(fileName) await expect(comfyPage.canvas).toHaveScreenshot(`${fileName}.png`) }) }) + + const urls = [ + 'https://comfyanonymous.github.io/ComfyUI_examples/hidream/hidream_dev_example.png' + ] + urls.forEach(async (url) => { + test(`Load workflow from URL${url} (drop from different browser tabs)`, async ({ + comfyPage + }) => { + await comfyPage.dragAndDropURL(url) + const readableName = url.split('/').pop() + await expect(comfyPage.canvas).toHaveScreenshot( + `dropped_workflow_url_${readableName}.png` + ) + }) + }) }) diff --git a/browser_tests/tests/loadWorkflowInMedia.spec.ts-snapshots/dropped-workflow-url-hidream-dev-example-png-chromium-linux.png b/browser_tests/tests/loadWorkflowInMedia.spec.ts-snapshots/dropped-workflow-url-hidream-dev-example-png-chromium-linux.png new file mode 100644 index 0000000000..26c12f175b Binary files /dev/null and b/browser_tests/tests/loadWorkflowInMedia.spec.ts-snapshots/dropped-workflow-url-hidream-dev-example-png-chromium-linux.png differ diff --git a/src/scripts/app.ts b/src/scripts/app.ts index 12215633a8..598d34d7cd 100644 --- a/src/scripts/app.ts +++ b/src/scripts/app.ts @@ -481,9 +481,8 @@ export class ComfyApp { if (match) { const uri = event.dataTransfer.getData(match)?.split('\n')?.[0] if (uri) { - await this.handleFile( - new File([await (await fetch(uri)).blob()], uri) - ) + const blob = await (await fetch(uri)).blob() + await this.handleFile(new File([blob], uri, { type: blob.type })) } } }