mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-07-15 19:54:32 +00:00
## Summary Narrow Knip's Playwright `entry` to actual spec files so dead exports in browser-test fixtures are reported instead of being hidden by treating every helper as an entrypoint. ## Changes - **What**: - `knip.config.ts`: Playwright `entry` changed from the broad `['**/*.@(spec|test)…', 'browser_tests/**/*.ts']` to `['browser_tests/**/*.@(spec|test).?(c|m)[jt]s?(x)']`. `globalSetup`/`globalTeardown` stay covered via Knip's playwright config resolution; fixtures remain in the project graph so their unused exports surface. - Resolved the 54 dead findings this exposed: over-exported symbols used only within their own module are now module-private (dropped `export`, no behavioral change); genuinely unreferenced fixtures were deleted (asset/template `ALL_*` aggregators + orphaned `STABLE_*` data, `TemplateHelper` distribution helpers + `generateTemplates`, dead types/utils, and the unused `nodeDefinitions.ts` module). - **Breaking**: none — test-only changes. ## Review Focus - Deletions are limited to fixtures with zero importers on `main` (verified via `pnpm knip`); the bulk of the diff is `export`-keyword removal. - Verified: `pnpm knip` (browser_tests clean), `pnpm typecheck`, `pnpm typecheck:browser`, oxfmt/oxlint/eslint all pass. Linear: FE-717
137 lines
4.1 KiB
TypeScript
137 lines
4.1 KiB
TypeScript
import type { Locator } from '@playwright/test'
|
|
import { expect } from '@playwright/test'
|
|
|
|
import { comfyPageFixture } from '@e2e/fixtures/ComfyPage'
|
|
import type { ComfyPage } from '@e2e/fixtures/ComfyPage'
|
|
|
|
const MASK_CANVAS_INDEX = 2
|
|
const RGB_CANVAS_INDEX = 1
|
|
|
|
type BrushSliderLabel = 'thickness'
|
|
|
|
class MaskEditorHelper {
|
|
constructor(private comfyPage: ComfyPage) {}
|
|
|
|
private get page() {
|
|
return this.comfyPage.page
|
|
}
|
|
|
|
async loadImageOnNode() {
|
|
await this.comfyPage.workflow.loadWorkflow('widgets/load_image_widget')
|
|
|
|
const loadImageNode = (
|
|
await this.comfyPage.nodeOps.getNodeRefsByType('LoadImage')
|
|
)[0]
|
|
const { x, y } = await loadImageNode.getPosition()
|
|
|
|
await this.comfyPage.dragDrop.dragAndDropFile('image64x64.webp', {
|
|
dropPosition: { x, y }
|
|
})
|
|
|
|
const imagePreview = this.page.locator('.image-preview')
|
|
await expect(imagePreview).toBeVisible()
|
|
await expect(imagePreview.locator('img')).toBeVisible()
|
|
await expect(imagePreview).toContainText('x')
|
|
|
|
return {
|
|
imagePreview,
|
|
nodeId: String(loadImageNode.id)
|
|
}
|
|
}
|
|
|
|
async openDialog(): Promise<Locator> {
|
|
const { imagePreview } = await this.loadImageOnNode()
|
|
|
|
await imagePreview.getByRole('region').hover()
|
|
await this.page.getByLabel('Edit or mask image').click()
|
|
|
|
const dialog = this.page.locator('.mask-editor-dialog')
|
|
await expect(dialog).toBeVisible()
|
|
await expect(
|
|
dialog.getByRole('heading', { name: 'Mask Editor' })
|
|
).toBeVisible()
|
|
|
|
const canvasContainer = dialog.locator('#maskEditorCanvasContainer')
|
|
await expect(canvasContainer).toBeVisible()
|
|
await expect(canvasContainer.locator('canvas')).toHaveCount(4)
|
|
|
|
return dialog
|
|
}
|
|
|
|
async drawStrokeOnPointerZone(dialog: Locator) {
|
|
const pointerZone = dialog.getByTestId('pointer-zone')
|
|
await expect(pointerZone).toBeVisible()
|
|
|
|
const box = await pointerZone.boundingBox()
|
|
if (!box) throw new Error('Pointer zone bounding box not found')
|
|
|
|
const startX = box.x + box.width * 0.3
|
|
const startY = box.y + box.height * 0.5
|
|
const endX = box.x + box.width * 0.7
|
|
const endY = box.y + box.height * 0.5
|
|
|
|
await this.page.mouse.move(startX, startY)
|
|
await this.page.mouse.down()
|
|
await this.page.mouse.move(endX, endY, { steps: 10 })
|
|
await this.page.mouse.up()
|
|
|
|
return { startX, startY, endX, endY, box }
|
|
}
|
|
|
|
async drawStrokeAndExpectPixels(dialog: Locator) {
|
|
await this.drawStrokeOnPointerZone(dialog)
|
|
await expect.poll(() => this.pollMaskPixelCount()).toBeGreaterThan(0)
|
|
}
|
|
|
|
getCanvasPixelData(canvasIndex: number) {
|
|
return this.page.evaluate((idx) => {
|
|
const canvases = document.querySelectorAll(
|
|
'#maskEditorCanvasContainer canvas'
|
|
)
|
|
const canvas = canvases[idx] as HTMLCanvasElement | undefined
|
|
if (!canvas) return null
|
|
const ctx = canvas.getContext('2d')
|
|
if (!ctx) return null
|
|
const data = ctx.getImageData(0, 0, canvas.width, canvas.height)
|
|
let nonTransparentPixels = 0
|
|
for (let i = 3; i < data.data.length; i += 4) {
|
|
if (data.data[i] > 0) nonTransparentPixels++
|
|
}
|
|
return { nonTransparentPixels, totalPixels: data.data.length / 4 }
|
|
}, canvasIndex)
|
|
}
|
|
|
|
pollMaskPixelCount(): Promise<number> {
|
|
return this.getCanvasPixelData(MASK_CANVAS_INDEX).then(
|
|
(d) => d?.nonTransparentPixels ?? 0
|
|
)
|
|
}
|
|
|
|
pollRgbPixelCount(): Promise<number> {
|
|
return this.getCanvasPixelData(RGB_CANVAS_INDEX).then(
|
|
(d) => d?.nonTransparentPixels ?? 0
|
|
)
|
|
}
|
|
|
|
getCanvasSnapshot(canvasIndex: number): Promise<string> {
|
|
return this.page.evaluate((idx) => {
|
|
const canvas = document.querySelectorAll(
|
|
'#maskEditorCanvasContainer canvas'
|
|
)[idx] as HTMLCanvasElement | undefined
|
|
return canvas?.toDataURL() ?? ''
|
|
}, canvasIndex)
|
|
}
|
|
|
|
brushInput(dialog: Locator, label: BrushSliderLabel): Locator {
|
|
return dialog.getByTestId(`brush-${label}-input`)
|
|
}
|
|
}
|
|
|
|
export const maskEditorTest = comfyPageFixture.extend<{
|
|
maskEditor: MaskEditorHelper
|
|
}>({
|
|
maskEditor: async ({ comfyPage }, use) => {
|
|
await use(new MaskEditorHelper(comfyPage))
|
|
}
|
|
})
|