mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-20 06:20:11 +00:00
### Summary of Improvements
* **Custom Test Coverage Extension**: Enhanced the Painter widget E2E
test suite by refactoring logic for better maintainability and
robustness.
* **Stable Component Targeting**: Introduced
`data-testid="painter-dimension-text"` to `WidgetPainter.vue`, providing
a reliable, non-CSS-dependent locator for canvas size verification.
* **Improved Test Organization**: Reorganized existing test scenarios
into logical categories using `test.describe` blocks (Drawing, Brush
Settings, Canvas Size Controls, etc.).
* **Asynchronous Helper Integration**: Converted `hasCanvasContent` to
an asynchronous helper and unified its usage across multiple test cases
to eliminate redundant pixel-checking logic.
* **Locator Resilience**: Updated Reka UI slider interaction logic to
use more precise targeting (`:not([data-slot])`), preventing ambiguity
and improving test stability.
* **Scenario Refinement**: Updated the `pointerup` test logic to
accurately reflect pointer capture behavior when interactions occur
outside the canvas boundaries.
* **Enhanced Verification Feedback**: Added descriptive error messages
to `expect.poll` assertions to provide clearer context on potential
failure points.
* **Standardized Tagging**: Restored the original tagging strategy
(including `@smoke` and `@screenshot` tags) to ensure tests are
categorized correctly for CI environments.
### Red-Green Verification
| Commit | CI Status | Purpose |
| :--- | :--- | :--- |
| `test: refactor painter widget e2e tests and address review findings`
| 🟢 Green | Addresses all E2E test quality and stability issues from
review findings. |
### Test Plan
- [x] **Quality Checks**: `pnpm format`, `pnpm lint`, and `pnpm
typecheck` verified as passing.
- [x] **Component Integration**: `WidgetPainter.vue` `data-testid`
correctly applied and used in tests.
- [x] **Helper Reliability**: `hasCanvasContent` correctly identifies
colored pixels and returns a promise for `expect.poll`.
- [x] **Locator Robustness**: Verified Reka slider locators correctly
exclude internal thumb spans.
- [x] **Boundary Interaction**: Verified `pointerup` correctly ends
strokes when triggered outside the viewport.
- [x] **Tagging Consistency**: Verified `@smoke` and `@screenshot` tags
are present in the final test suite.
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10846-test-Painter-Widget-E2E-Test-Plan-3386d73d365081deb70fe4afbd417efb)
by [Unito](https://www.unito.io)
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> **Low Risk**
> Primarily adds/refactors Playwright E2E tests and stable `data-testid`
hooks, with no changes to Painter drawing logic. Risk is limited to
potential test brittleness or minor UI attribute changes.
>
> **Overview**
> Expands the Painter widget Playwright suite with new grouped scenarios
covering drawing/erasing behavior, tool switching, brush inputs, canvas
resizing (including preserving drawings), clear behavior, and
serialization/upload flows (including failure toast).
>
> Refactors the tests to use a shared `@e2e/helpers/painter` module
(`drawStroke`, `hasCanvasContent`, `triggerSerialization`), improves
stability via role/testid-based locators and clearer `expect.poll`
messaging, and adds `data-testid` attributes (e.g.,
`painter-clear-button`, `painter-*-row`, `painter-dimension-text`) to
`WidgetPainter.vue` to avoid CSS-dependent selectors.
>
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
053a8e9ed2. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
---------
Co-authored-by: GitHub Action <action@github.com>
67 lines
1.9 KiB
TypeScript
67 lines
1.9 KiB
TypeScript
import type { Locator, Page } from '@playwright/test'
|
|
import type { TestGraphAccess } from '@e2e/types/globals'
|
|
|
|
export async function drawStroke(
|
|
page: Page,
|
|
canvas: Locator,
|
|
opts: { startXPct?: number; endXPct?: number; yPct?: number } = {}
|
|
): Promise<void> {
|
|
const { startXPct = 0.3, endXPct = 0.7, yPct = 0.5 } = opts
|
|
const box = await canvas.boundingBox()
|
|
if (!box) throw new Error('Canvas bounding box not found')
|
|
await page.mouse.move(
|
|
box.x + box.width * startXPct,
|
|
box.y + box.height * yPct
|
|
)
|
|
await page.mouse.down()
|
|
await page.mouse.move(
|
|
box.x + box.width * endXPct,
|
|
box.y + box.height * yPct,
|
|
{ steps: 10 }
|
|
)
|
|
await page.mouse.up()
|
|
}
|
|
|
|
export async function hasCanvasContent(canvas: Locator): Promise<boolean> {
|
|
return canvas.evaluate((el: HTMLCanvasElement) => {
|
|
const ctx = el.getContext('2d')
|
|
if (!ctx) return false
|
|
const { data } = ctx.getImageData(0, 0, el.width, el.height)
|
|
for (let i = 3; i < data.length; i += 4) {
|
|
if (data[i] > 0) return true
|
|
}
|
|
return false
|
|
})
|
|
}
|
|
|
|
export async function triggerSerialization(page: Page): Promise<void> {
|
|
await page.evaluate(async () => {
|
|
const graph = window.graph as TestGraphAccess | undefined
|
|
if (!graph) {
|
|
throw new Error(
|
|
'Global window.graph is absent. Ensure workflow fixture is loaded.'
|
|
)
|
|
}
|
|
|
|
const node = graph._nodes_by_id?.['1']
|
|
if (!node) {
|
|
throw new Error(
|
|
'Target node with ID "1" not found in graph._nodes_by_id.'
|
|
)
|
|
}
|
|
|
|
const widget = node.widgets?.find((w) => w.name === 'mask')
|
|
if (!widget) {
|
|
throw new Error('Widget "mask" not found on target node 1.')
|
|
}
|
|
|
|
if (typeof widget.serializeValue !== 'function') {
|
|
throw new Error(
|
|
'mask widget on node 1 does not have a serializeValue function.'
|
|
)
|
|
}
|
|
|
|
await widget.serializeValue(node, 0)
|
|
})
|
|
}
|