mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-29 18:52:19 +00:00
refactor: extract DebugHelper from ComfyPage.ts
- Create browser_tests/fixtures/helpers/DebugHelper.ts with 7 methods - Add debug: DebugHelper property to ComfyPage - Replace debug methods with deprecation proxies delegating to this.debug - Reduces ComfyPage.ts by ~140 lines Amp-Thread-ID: https://ampcode.com/threads/T-019c1206-3625-71ef-b976-1796574b9642 Co-authored-by: Amp <amp@ampcode.com>
This commit is contained in:
@@ -20,6 +20,7 @@ import {
|
|||||||
} from './components/SidebarTab'
|
} from './components/SidebarTab'
|
||||||
import { Topbar } from './components/Topbar'
|
import { Topbar } from './components/Topbar'
|
||||||
import { DefaultGraphPositions } from './constants/defaultGraphPositions'
|
import { DefaultGraphPositions } from './constants/defaultGraphPositions'
|
||||||
|
import { DebugHelper } from './helpers/DebugHelper'
|
||||||
import type { Position, Size } from './types'
|
import type { Position, Size } from './types'
|
||||||
import { NodeReference, SubgraphSlotReference } from './utils/litegraphUtils'
|
import { NodeReference, SubgraphSlotReference } from './utils/litegraphUtils'
|
||||||
|
|
||||||
@@ -171,6 +172,7 @@ export class ComfyPage {
|
|||||||
public readonly settingDialog: SettingDialog
|
public readonly settingDialog: SettingDialog
|
||||||
public readonly confirmDialog: ConfirmDialog
|
public readonly confirmDialog: ConfirmDialog
|
||||||
public readonly vueNodes: VueNodeHelpers
|
public readonly vueNodes: VueNodeHelpers
|
||||||
|
public readonly debug: DebugHelper
|
||||||
|
|
||||||
/** Worker index to test user ID */
|
/** Worker index to test user ID */
|
||||||
public readonly userIds: string[] = []
|
public readonly userIds: string[] = []
|
||||||
@@ -203,6 +205,7 @@ export class ComfyPage {
|
|||||||
this.settingDialog = new SettingDialog(page, this)
|
this.settingDialog = new SettingDialog(page, this)
|
||||||
this.confirmDialog = new ConfirmDialog(page)
|
this.confirmDialog = new ConfirmDialog(page)
|
||||||
this.vueNodes = new VueNodeHelpers(page)
|
this.vueNodes = new VueNodeHelpers(page)
|
||||||
|
this.debug = new DebugHelper(page, this.canvas)
|
||||||
}
|
}
|
||||||
|
|
||||||
convertLeafToContent(structure: FolderStructure): FolderStructure {
|
convertLeafToContent(structure: FolderStructure): FolderStructure {
|
||||||
@@ -1153,57 +1156,20 @@ export class ComfyPage {
|
|||||||
await this.nextFrame()
|
await this.nextFrame()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @deprecated Use this.debug.addMarker() instead */
|
||||||
* Add a visual marker at a position for debugging
|
|
||||||
*/
|
|
||||||
async debugAddMarker(
|
async debugAddMarker(
|
||||||
position: Position,
|
position: Position,
|
||||||
id: string = 'debug-marker'
|
id: string = 'debug-marker'
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
await this.page.evaluate(
|
return this.debug.addMarker(position, id)
|
||||||
([pos, markerId]) => {
|
|
||||||
// Remove existing marker if present
|
|
||||||
const existing = document.getElementById(markerId)
|
|
||||||
if (existing) existing.remove()
|
|
||||||
|
|
||||||
// Create marker
|
|
||||||
const marker = document.createElement('div')
|
|
||||||
marker.id = markerId
|
|
||||||
marker.style.position = 'fixed'
|
|
||||||
marker.style.left = `${pos.x - 10}px`
|
|
||||||
marker.style.top = `${pos.y - 10}px`
|
|
||||||
marker.style.width = '20px'
|
|
||||||
marker.style.height = '20px'
|
|
||||||
marker.style.border = '2px solid red'
|
|
||||||
marker.style.borderRadius = '50%'
|
|
||||||
marker.style.backgroundColor = 'rgba(255, 0, 0, 0.3)'
|
|
||||||
marker.style.pointerEvents = 'none'
|
|
||||||
marker.style.zIndex = '10000'
|
|
||||||
document.body.appendChild(marker)
|
|
||||||
},
|
|
||||||
[position, id] as const
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @deprecated Use this.debug.removeMarkers() instead */
|
||||||
* Remove debug markers
|
|
||||||
*/
|
|
||||||
async debugRemoveMarkers(): Promise<void> {
|
async debugRemoveMarkers(): Promise<void> {
|
||||||
await this.page.evaluate(() => {
|
return this.debug.removeMarkers()
|
||||||
document
|
|
||||||
.querySelectorAll('[id^="debug-marker"]')
|
|
||||||
.forEach((el) => el.remove())
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @deprecated Use this.debug.attachScreenshot() instead */
|
||||||
* Take a screenshot and attach it to the test report for debugging
|
|
||||||
* This is a convenience method that combines screenshot capture and test attachment
|
|
||||||
*
|
|
||||||
* @param testInfo The Playwright TestInfo object (from test parameters)
|
|
||||||
* @param name Name for the attachment
|
|
||||||
* @param options Optional screenshot options (defaults to page screenshot)
|
|
||||||
*/
|
|
||||||
async debugAttachScreenshot(
|
async debugAttachScreenshot(
|
||||||
testInfo: any,
|
testInfo: any,
|
||||||
name: string,
|
name: string,
|
||||||
@@ -1213,35 +1179,7 @@ export class ComfyPage {
|
|||||||
markers?: Array<{ position: Position; id?: string }>
|
markers?: Array<{ position: Position; id?: string }>
|
||||||
}
|
}
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
// Add markers if requested
|
return this.debug.attachScreenshot(testInfo, name, options)
|
||||||
if (options?.markers) {
|
|
||||||
for (const marker of options.markers) {
|
|
||||||
await this.debugAddMarker(marker.position, marker.id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Take screenshot - default to page if not specified
|
|
||||||
let screenshot: Buffer
|
|
||||||
const targetElement = options?.element || 'page'
|
|
||||||
|
|
||||||
if (targetElement === 'canvas') {
|
|
||||||
screenshot = await this.canvas.screenshot()
|
|
||||||
} else if (options?.fullPage) {
|
|
||||||
screenshot = await this.page.screenshot({ fullPage: true })
|
|
||||||
} else {
|
|
||||||
screenshot = await this.page.screenshot()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Attach to test report
|
|
||||||
await testInfo.attach(name, {
|
|
||||||
body: screenshot,
|
|
||||||
contentType: 'image/png'
|
|
||||||
})
|
|
||||||
|
|
||||||
// Clean up markers if we added any
|
|
||||||
if (options?.markers) {
|
|
||||||
await this.debugRemoveMarkers()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async doubleClickCanvas() {
|
async doubleClickCanvas() {
|
||||||
@@ -1249,105 +1187,24 @@ export class ComfyPage {
|
|||||||
await this.nextFrame()
|
await this.nextFrame()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @deprecated Use this.debug.saveCanvasScreenshot() instead */
|
||||||
* Capture the canvas as a PNG and save it for debugging
|
|
||||||
*/
|
|
||||||
async debugSaveCanvasScreenshot(filename: string): Promise<void> {
|
async debugSaveCanvasScreenshot(filename: string): Promise<void> {
|
||||||
await this.page.evaluate(async (filename) => {
|
return this.debug.saveCanvasScreenshot(filename)
|
||||||
const canvas = document.getElementById(
|
|
||||||
'graph-canvas'
|
|
||||||
) as HTMLCanvasElement
|
|
||||||
if (!canvas) {
|
|
||||||
throw new Error('Canvas not found')
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert canvas to blob
|
|
||||||
return new Promise<void>((resolve) => {
|
|
||||||
canvas.toBlob(async (blob) => {
|
|
||||||
if (!blob) {
|
|
||||||
throw new Error('Failed to create blob from canvas')
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a download link and trigger it
|
|
||||||
const url = URL.createObjectURL(blob)
|
|
||||||
const a = document.createElement('a')
|
|
||||||
a.href = url
|
|
||||||
a.download = filename
|
|
||||||
document.body.appendChild(a)
|
|
||||||
a.click()
|
|
||||||
document.body.removeChild(a)
|
|
||||||
URL.revokeObjectURL(url)
|
|
||||||
resolve()
|
|
||||||
}, 'image/png')
|
|
||||||
})
|
|
||||||
}, filename)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @deprecated Use this.debug.getCanvasDataURL() instead */
|
||||||
* Capture canvas as base64 data URL for inspection
|
|
||||||
*/
|
|
||||||
async debugGetCanvasDataURL(): Promise<string> {
|
async debugGetCanvasDataURL(): Promise<string> {
|
||||||
return await this.page.evaluate(() => {
|
return this.debug.getCanvasDataURL()
|
||||||
const canvas = document.getElementById(
|
|
||||||
'graph-canvas'
|
|
||||||
) as HTMLCanvasElement
|
|
||||||
if (!canvas) {
|
|
||||||
throw new Error('Canvas not found')
|
|
||||||
}
|
|
||||||
return canvas.toDataURL('image/png')
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @deprecated Use this.debug.showCanvasOverlay() instead */
|
||||||
* Create an overlay div with the canvas image for easier Playwright screenshot
|
|
||||||
*/
|
|
||||||
async debugShowCanvasOverlay(): Promise<void> {
|
async debugShowCanvasOverlay(): Promise<void> {
|
||||||
await this.page.evaluate(() => {
|
return this.debug.showCanvasOverlay()
|
||||||
const canvas = document.getElementById(
|
|
||||||
'graph-canvas'
|
|
||||||
) as HTMLCanvasElement
|
|
||||||
if (!canvas) {
|
|
||||||
throw new Error('Canvas not found')
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove existing overlay if present
|
|
||||||
const existingOverlay = document.getElementById('debug-canvas-overlay')
|
|
||||||
if (existingOverlay) {
|
|
||||||
existingOverlay.remove()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create overlay div
|
|
||||||
const overlay = document.createElement('div')
|
|
||||||
overlay.id = 'debug-canvas-overlay'
|
|
||||||
overlay.style.position = 'fixed'
|
|
||||||
overlay.style.top = '0'
|
|
||||||
overlay.style.left = '0'
|
|
||||||
overlay.style.zIndex = '9999'
|
|
||||||
overlay.style.backgroundColor = 'white'
|
|
||||||
overlay.style.padding = '10px'
|
|
||||||
overlay.style.border = '2px solid red'
|
|
||||||
|
|
||||||
// Create image from canvas
|
|
||||||
const img = document.createElement('img')
|
|
||||||
img.src = canvas.toDataURL('image/png')
|
|
||||||
img.style.maxWidth = '800px'
|
|
||||||
img.style.maxHeight = '600px'
|
|
||||||
overlay.appendChild(img)
|
|
||||||
|
|
||||||
document.body.appendChild(overlay)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @deprecated Use this.debug.hideCanvasOverlay() instead */
|
||||||
* Remove the debug canvas overlay
|
|
||||||
*/
|
|
||||||
async debugHideCanvasOverlay(): Promise<void> {
|
async debugHideCanvasOverlay(): Promise<void> {
|
||||||
await this.page.evaluate(() => {
|
return this.debug.hideCanvasOverlay()
|
||||||
const overlay = document.getElementById('debug-canvas-overlay')
|
|
||||||
if (overlay) {
|
|
||||||
overlay.remove()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async clickEmptyLatentNode() {
|
async clickEmptyLatentNode() {
|
||||||
|
|||||||
167
browser_tests/fixtures/helpers/DebugHelper.ts
Normal file
167
browser_tests/fixtures/helpers/DebugHelper.ts
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
import type { Locator, Page, TestInfo } from '@playwright/test'
|
||||||
|
|
||||||
|
import type { Position } from '../types'
|
||||||
|
|
||||||
|
export interface DebugScreenshotOptions {
|
||||||
|
fullPage?: boolean
|
||||||
|
element?: 'canvas' | 'page'
|
||||||
|
markers?: Array<{ position: Position; id?: string }>
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DebugHelper {
|
||||||
|
constructor(
|
||||||
|
private page: Page,
|
||||||
|
private canvas: Locator
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async addMarker(
|
||||||
|
position: Position,
|
||||||
|
id: string = 'debug-marker'
|
||||||
|
): Promise<void> {
|
||||||
|
await this.page.evaluate(
|
||||||
|
([pos, markerId]) => {
|
||||||
|
const existing = document.getElementById(markerId)
|
||||||
|
if (existing) existing.remove()
|
||||||
|
|
||||||
|
const marker = document.createElement('div')
|
||||||
|
marker.id = markerId
|
||||||
|
marker.style.position = 'fixed'
|
||||||
|
marker.style.left = `${pos.x - 10}px`
|
||||||
|
marker.style.top = `${pos.y - 10}px`
|
||||||
|
marker.style.width = '20px'
|
||||||
|
marker.style.height = '20px'
|
||||||
|
marker.style.border = '2px solid red'
|
||||||
|
marker.style.borderRadius = '50%'
|
||||||
|
marker.style.backgroundColor = 'rgba(255, 0, 0, 0.3)'
|
||||||
|
marker.style.pointerEvents = 'none'
|
||||||
|
marker.style.zIndex = '10000'
|
||||||
|
document.body.appendChild(marker)
|
||||||
|
},
|
||||||
|
[position, id] as const
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
async removeMarkers(): Promise<void> {
|
||||||
|
await this.page.evaluate(() => {
|
||||||
|
document
|
||||||
|
.querySelectorAll('[id^="debug-marker"]')
|
||||||
|
.forEach((el) => el.remove())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async attachScreenshot(
|
||||||
|
testInfo: TestInfo,
|
||||||
|
name: string,
|
||||||
|
options?: DebugScreenshotOptions
|
||||||
|
): Promise<void> {
|
||||||
|
if (options?.markers) {
|
||||||
|
for (const marker of options.markers) {
|
||||||
|
await this.addMarker(marker.position, marker.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let screenshot: Buffer
|
||||||
|
const targetElement = options?.element || 'page'
|
||||||
|
|
||||||
|
if (targetElement === 'canvas') {
|
||||||
|
screenshot = await this.canvas.screenshot()
|
||||||
|
} else if (options?.fullPage) {
|
||||||
|
screenshot = await this.page.screenshot({ fullPage: true })
|
||||||
|
} else {
|
||||||
|
screenshot = await this.page.screenshot()
|
||||||
|
}
|
||||||
|
|
||||||
|
await testInfo.attach(name, {
|
||||||
|
body: screenshot,
|
||||||
|
contentType: 'image/png'
|
||||||
|
})
|
||||||
|
|
||||||
|
if (options?.markers) {
|
||||||
|
await this.removeMarkers()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async saveCanvasScreenshot(filename: string): Promise<void> {
|
||||||
|
await this.page.evaluate(async (filename) => {
|
||||||
|
const canvas = document.getElementById(
|
||||||
|
'graph-canvas'
|
||||||
|
) as HTMLCanvasElement
|
||||||
|
if (!canvas) {
|
||||||
|
throw new Error('Canvas not found')
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Promise<void>((resolve) => {
|
||||||
|
canvas.toBlob(async (blob) => {
|
||||||
|
if (!blob) {
|
||||||
|
throw new Error('Failed to create blob from canvas')
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = URL.createObjectURL(blob)
|
||||||
|
const a = document.createElement('a')
|
||||||
|
a.href = url
|
||||||
|
a.download = filename
|
||||||
|
document.body.appendChild(a)
|
||||||
|
a.click()
|
||||||
|
document.body.removeChild(a)
|
||||||
|
URL.revokeObjectURL(url)
|
||||||
|
resolve()
|
||||||
|
}, 'image/png')
|
||||||
|
})
|
||||||
|
}, filename)
|
||||||
|
}
|
||||||
|
|
||||||
|
async getCanvasDataURL(): Promise<string> {
|
||||||
|
return await this.page.evaluate(() => {
|
||||||
|
const canvas = document.getElementById(
|
||||||
|
'graph-canvas'
|
||||||
|
) as HTMLCanvasElement
|
||||||
|
if (!canvas) {
|
||||||
|
throw new Error('Canvas not found')
|
||||||
|
}
|
||||||
|
return canvas.toDataURL('image/png')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async showCanvasOverlay(): Promise<void> {
|
||||||
|
await this.page.evaluate(() => {
|
||||||
|
const canvas = document.getElementById(
|
||||||
|
'graph-canvas'
|
||||||
|
) as HTMLCanvasElement
|
||||||
|
if (!canvas) {
|
||||||
|
throw new Error('Canvas not found')
|
||||||
|
}
|
||||||
|
|
||||||
|
const existingOverlay = document.getElementById('debug-canvas-overlay')
|
||||||
|
if (existingOverlay) {
|
||||||
|
existingOverlay.remove()
|
||||||
|
}
|
||||||
|
|
||||||
|
const overlay = document.createElement('div')
|
||||||
|
overlay.id = 'debug-canvas-overlay'
|
||||||
|
overlay.style.position = 'fixed'
|
||||||
|
overlay.style.top = '0'
|
||||||
|
overlay.style.left = '0'
|
||||||
|
overlay.style.zIndex = '9999'
|
||||||
|
overlay.style.backgroundColor = 'white'
|
||||||
|
overlay.style.padding = '10px'
|
||||||
|
overlay.style.border = '2px solid red'
|
||||||
|
|
||||||
|
const img = document.createElement('img')
|
||||||
|
img.src = canvas.toDataURL('image/png')
|
||||||
|
img.style.maxWidth = '800px'
|
||||||
|
img.style.maxHeight = '600px'
|
||||||
|
overlay.appendChild(img)
|
||||||
|
|
||||||
|
document.body.appendChild(overlay)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async hideCanvasOverlay(): Promise<void> {
|
||||||
|
await this.page.evaluate(() => {
|
||||||
|
const overlay = document.getElementById('debug-canvas-overlay')
|
||||||
|
if (overlay) {
|
||||||
|
overlay.remove()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user