Files
ComfyUI_frontend/browser_tests/tests/imageCrop.spec.ts
Dante 19fff29204 test: backfill e2e coverage gaps for toolkit widgets, minimap, mask editor, painter (#11183)
## Summary

Backfills missing e2e test coverage identified in the [FixIt
Burndown](https://www.notion.so/comfy-org/FixIt-Burndown-32e6d73d365080609a81cdc9bc884460)
audit. Adds 39 new behavioral tests across 5 spec files with zero
test-code overlap.

## Changes

- **What**: New e2e specs for Image Crop (6 tests) and Curve Widget (6
tests). Deepened coverage for Minimap (+6), Mask Editor (+10), Painter
(+11).
- **New fixtures**: `curve_widget.json`, updated
`image_crop_widget.json`

## Test Inventory

| Spec | New tests | Coverage area |
|---|---|---|
| `imageCrop.spec.ts` | 6 | Empty state, bounding box inputs, ratio
selector/presets, lock toggle, programmatic value update |
| `curveWidget.spec.ts` | 6 | SVG render, click-to-add point,
drag-to-reshape, Ctrl+click remove, interpolation mode switch, min-2
guard |
| `minimap.spec.ts` | +6 | Click-to-pan, drag-to-pan, zoom viewport
shrink, node count changes, workflow reload, pan state reflection |
| `maskEditor.spec.ts` | +10 | Brush drawing, undo/redo, clear, cancel,
invert, Ctrl+Z, tool panel/switching, brush settings, save with mock,
eraser |
| `painter.spec.ts` | +11 | Clear, eraser, control visibility toggle,
brush size slider, stroke width comparison, canvas dimensions,
background color, multi-stroke accumulate, color picker, opacity,
partial erase |

## Review Focus

- Mask editor tests use `.maskEditor_toolPanelContainer` class selectors
— may need test-id hardening later
- Painter slider interaction tests could be flaky if slider layout
changes
- All canvas pixel-count assertions use `expect.poll()` with timeouts
for reliability

## Test plan
- [ ] CI passes all new/modified specs
- [ ] No duplicate coverage with existing tests (verified via grep
before writing)
- [ ] No `waitForTimeout` usage (confirmed)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-11183-test-backfill-e2e-coverage-gaps-for-toolkit-widgets-minimap-mask-editor-painter-3416d73d3650819ca33edd1f27b9651a)
by [Unito](https://www.unito.io)

---------

Co-authored-by: GitHub Action <action@github.com>
2026-04-16 09:48:13 +00:00

123 lines
3.6 KiB
TypeScript

import { expect } from '@playwright/test'
import { comfyPageFixture as test } from '@e2e/fixtures/ComfyPage'
test.describe('Image Crop', () => {
test.beforeEach(async ({ comfyPage }) => {
await comfyPage.settings.setSetting('Comfy.VueNodes.Enabled', true)
await comfyPage.workflow.loadWorkflow('widgets/image_crop_widget')
await comfyPage.vueNodes.waitForNodes()
})
test(
'Shows empty state when no input image is connected',
{ tag: '@smoke' },
async ({ comfyPage }) => {
const node = comfyPage.vueNodes.getNodeLocator('1')
await expect(node).toBeVisible()
await expect(node.getByText('No input image connected')).toBeVisible()
await expect(node.locator('img[alt="Crop preview"]')).toHaveCount(0)
}
)
test(
'Renders bounding box coordinate inputs',
{ tag: '@smoke' },
async ({ comfyPage }) => {
const node = comfyPage.vueNodes.getNodeLocator('1')
await expect(node).toBeVisible()
await expect(node.getByText('X')).toBeVisible()
await expect(node.getByText('Y')).toBeVisible()
await expect(node.getByText('Width')).toBeVisible()
await expect(node.getByText('Height')).toBeVisible()
}
)
test(
'Renders ratio selector and lock button',
{ tag: '@ui' },
async ({ comfyPage }) => {
const node = comfyPage.vueNodes.getNodeLocator('1')
await expect(node).toBeVisible()
await expect(node.getByText('Ratio')).toBeVisible()
await expect(node.getByRole('button', { name: /lock/i })).toBeVisible()
}
)
test(
'Lock button toggles aspect ratio lock',
{ tag: '@ui' },
async ({ comfyPage }) => {
const node = comfyPage.vueNodes.getNodeLocator('1')
const lockButton = node.getByRole('button', {
name: 'Lock aspect ratio'
})
await expect(lockButton).toBeVisible()
await lockButton.click()
await expect(
node.getByRole('button', { name: 'Unlock aspect ratio' })
).toBeVisible()
await node.getByRole('button', { name: 'Unlock aspect ratio' }).click()
await expect(
node.getByRole('button', { name: 'Lock aspect ratio' })
).toBeVisible()
}
)
test(
'Ratio selector offers expected presets',
{ tag: '@ui' },
async ({ comfyPage }) => {
const node = comfyPage.vueNodes.getNodeLocator('1')
const trigger = node.getByRole('combobox')
await trigger.click()
const expectedRatios = ['1:1', '3:4', '4:3', '16:9', '9:16', 'Custom']
for (const label of expectedRatios) {
await expect(
comfyPage.page.getByRole('option', { name: label, exact: true })
).toBeVisible()
}
}
)
test(
'Programmatically setting widget value updates bounding box inputs',
{ tag: '@ui' },
async ({ comfyPage }) => {
const newBounds = { x: 50, y: 100, width: 200, height: 300 }
await comfyPage.page.evaluate(
({ bounds }) => {
const node = window.app!.graph.getNodeById(1)
const widget = node?.widgets?.find((w) => w.type === 'imagecrop')
if (widget) {
widget.value = bounds
widget.callback?.(bounds)
}
},
{ bounds: newBounds }
)
await comfyPage.nextFrame()
const node = comfyPage.vueNodes.getNodeLocator('1')
const inputs = node.locator('input[inputmode="decimal"]')
await expect.poll(() => inputs.nth(0).inputValue()).toBe('50')
await expect.poll(() => inputs.nth(1).inputValue()).toBe('100')
await expect.poll(() => inputs.nth(2).inputValue()).toBe('200')
await expect.poll(() => inputs.nth(3).inputValue()).toBe('300')
}
)
})