mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-19 22:09:37 +00:00
## Summary Extract `assetPath` from a `ComfyPage` method to a standalone pure function, removing unnecessary coupling to the page object. ## Changes - **What**: Moved `assetPath` to `browser_tests/fixtures/utils/paths.ts`. `DragDropHelper` and `WorkflowHelper` import it directly instead of receiving it via `ComfyPage`. `ComfyPage.assetPath` kept as thin delegate for backward compat. ## Review Focus Structural-only refactor — no behavioral changes. The function was already pure (no `this`/`page` usage). ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-10651-refactor-extract-assetPath-as-standalone-pure-function-3316d73d365081c0b0e0ce6dde57ef8e) by [Unito](https://www.unito.io)
168 lines
4.5 KiB
TypeScript
168 lines
4.5 KiB
TypeScript
import { readFileSync } from 'fs'
|
|
|
|
import type { Page } from '@playwright/test'
|
|
|
|
import type { Position } from '../types'
|
|
import { getMimeType } from './mimeTypeUtil'
|
|
import { assetPath } from '../utils/paths'
|
|
|
|
export class DragDropHelper {
|
|
constructor(private readonly page: Page) {}
|
|
|
|
private async nextFrame(): Promise<void> {
|
|
await this.page.evaluate(() => {
|
|
return new Promise<void>((resolve) => {
|
|
requestAnimationFrame(() => resolve())
|
|
})
|
|
})
|
|
}
|
|
|
|
async dragAndDropExternalResource(
|
|
options: {
|
|
fileName?: string
|
|
url?: string
|
|
dropPosition?: Position
|
|
waitForUpload?: boolean
|
|
preserveNativePropagation?: boolean
|
|
} = {}
|
|
): Promise<void> {
|
|
const {
|
|
dropPosition = { x: 100, y: 100 },
|
|
fileName,
|
|
url,
|
|
waitForUpload = false,
|
|
preserveNativePropagation = false
|
|
} = options
|
|
|
|
if (!fileName && !url)
|
|
throw new Error('Must provide either fileName or url')
|
|
|
|
const evaluateParams: {
|
|
dropPosition: Position
|
|
fileName?: string
|
|
fileType?: string
|
|
buffer?: Uint8Array | number[]
|
|
url?: string
|
|
preserveNativePropagation: boolean
|
|
} = { dropPosition, preserveNativePropagation }
|
|
|
|
if (fileName) {
|
|
const filePath = assetPath(fileName)
|
|
const buffer = readFileSync(filePath)
|
|
|
|
evaluateParams.fileName = fileName
|
|
evaluateParams.fileType = getMimeType(fileName)
|
|
evaluateParams.buffer = [...new Uint8Array(buffer)]
|
|
}
|
|
|
|
if (url) evaluateParams.url = url
|
|
|
|
const uploadResponsePromise = waitForUpload
|
|
? this.page.waitForResponse(
|
|
(resp) => resp.url().includes('/upload/') && resp.status() === 200,
|
|
{ timeout: 10000 }
|
|
)
|
|
: null
|
|
|
|
await this.page.evaluate(async (params) => {
|
|
const dataTransfer = new DataTransfer()
|
|
|
|
if (params.buffer && params.fileName && params.fileType) {
|
|
const file = new File(
|
|
[new Uint8Array(params.buffer)],
|
|
params.fileName,
|
|
{
|
|
type: params.fileType
|
|
}
|
|
)
|
|
dataTransfer.items.add(file)
|
|
}
|
|
|
|
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) {
|
|
throw new Error(
|
|
`No element found at drop position: (${params.dropPosition.x}, ${params.dropPosition.y}). ` +
|
|
`document.elementFromPoint returned null. Ensure the target is visible and not obscured.`
|
|
)
|
|
}
|
|
|
|
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)
|
|
|
|
const graphCanvasElement = document.querySelector('#graph-canvas')
|
|
|
|
// Keep Litegraph's drag-over node tracking in sync when the drop target is a
|
|
// Vue node DOM overlay outside of the graph canvas element.
|
|
if (graphCanvasElement && !graphCanvasElement.contains(targetElement)) {
|
|
graphCanvasElement.dispatchEvent(
|
|
new DragEvent('dragover', eventOptions)
|
|
)
|
|
}
|
|
|
|
if (!params.preserveNativePropagation) {
|
|
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)
|
|
}
|
|
}
|
|
}, evaluateParams)
|
|
|
|
if (uploadResponsePromise) {
|
|
await uploadResponsePromise
|
|
}
|
|
|
|
await this.nextFrame()
|
|
}
|
|
|
|
async dragAndDropFile(
|
|
fileName: string,
|
|
options: { dropPosition?: Position; waitForUpload?: boolean } = {}
|
|
): Promise<void> {
|
|
return this.dragAndDropExternalResource({ fileName, ...options })
|
|
}
|
|
|
|
async dragAndDropURL(
|
|
url: string,
|
|
options: {
|
|
dropPosition?: Position
|
|
preserveNativePropagation?: boolean
|
|
} = {}
|
|
): Promise<void> {
|
|
return this.dragAndDropExternalResource({ url, ...options })
|
|
}
|
|
}
|