mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-20 06:20:11 +00:00
fix: refactor codegen-transform skill to reference existing docs
Addresses review feedback: https://github.com/Comfy-Org/ComfyUI_frontend/pull/10694#discussion_r3005775537
This commit is contained in:
@@ -14,43 +14,32 @@ Transform raw Playwright codegen output into tests that follow ComfyUI conventio
|
||||
- Agent needs to post-process Playwright test agent output
|
||||
- Reviewing a test that uses raw `page.*` calls instead of fixture helpers
|
||||
|
||||
## Reference Documentation
|
||||
|
||||
Before transforming, read these existing docs for full context:
|
||||
|
||||
| Document | What it covers |
|
||||
| --- | --- |
|
||||
| `docs/guidance/playwright.md` | Playwright conventions, type assertions, assertion best practices, tags |
|
||||
| `browser_tests/AGENTS.md` | Directory structure, polling assertions, gotchas, quality checks |
|
||||
| `browser_tests/fixtures/ComfyPage.ts` | Main fixture API (source of truth for all helpers) |
|
||||
| `browser_tests/fixtures/helpers/` | Focused helper classes (canvas, keyboard, workflow, etc.) |
|
||||
|
||||
## Transform Rules
|
||||
|
||||
Apply these replacements in order:
|
||||
The programmatic transform engine lives in `tools/test-recorder/src/transform/rules.ts`. Apply these replacements in order:
|
||||
|
||||
| Raw codegen | Convention replacement | Why |
|
||||
| ------------------------------------------------- | ----------------------------------------------------------------------------------------- | ---------------------------------------- |
|
||||
| Raw codegen | Convention replacement | Why |
|
||||
| --- | --- | --- |
|
||||
| `import { test, expect } from '@playwright/test'` | `import { comfyPageFixture as test, comfyExpect as expect } from '../fixtures/ComfyPage'` | Use custom fixtures with ComfyUI helpers |
|
||||
| `test('test', async ({ page }) =>` | `test('descriptive-name', async ({ comfyPage }) =>` | Use comfyPage fixture, descriptive names |
|
||||
| `await page.goto('http://...')` | **Remove entirely** | Fixture handles navigation automatically |
|
||||
| `page.locator('canvas')` | `comfyPage.canvas` | Pre-configured canvas locator |
|
||||
| `page.waitForTimeout(N)` | `comfyPage.nextFrame()` | Never use arbitrary waits |
|
||||
| `page.getByPlaceholder('Search Nodes...')` | `comfyPage.searchBox.input` | Use search box page object |
|
||||
| `page` (bare reference) | `comfyPage.page` | Access raw page through fixture |
|
||||
| Bare `test(...)` | `test.describe('Feature', { tag: ['@canvas'] }, () => { test(...) })` | All tests need describe + tags |
|
||||
| No cleanup | Add `test.afterEach(async ({ comfyPage }) => { await comfyPage.canvasOps.resetView() })` | Canvas tests need cleanup |
|
||||
|
||||
## Fixture API Quick Reference
|
||||
|
||||
| Need | Use | Notes |
|
||||
| ----------------- | ------------------------------------------------- | ------------------------------------------------------------- |
|
||||
| Canvas element | `comfyPage.canvas` | Pre-configured Locator |
|
||||
| Wait for render | `comfyPage.nextFrame()` | After canvas mutations. NOT needed after `loadWorkflow()` |
|
||||
| Load workflow | `comfyPage.workflow.loadWorkflow('name')` | Assets in `browser_tests/assets/` |
|
||||
| Get node by type | `comfyPage.nodeOps.getNodeRefsByType('KSampler')` | Returns array of NodeReference |
|
||||
| Get node by title | `comfyPage.nodeOps.getNodeRefsByTitle('My Node')` | Returns array of NodeReference |
|
||||
| Search box | `comfyPage.searchBox` | Has `.input`, `.fillAndSelectFirstNode()` |
|
||||
| Settings | `comfyPage.settings.setSetting(key, value)` | Persistent — clean up in afterEach |
|
||||
| Keyboard | `comfyPage.keyboard.press('Delete')` | Focus canvas first |
|
||||
| Drag & drop | `comfyPage.dragDrop.*` | Use `{ steps: 10 }` for reliability |
|
||||
| Context menu | `comfyPage.contextMenu.*` | Right-click interactions |
|
||||
| Toast messages | `comfyPage.toast.*` | Notification assertions |
|
||||
| Subgraph | `comfyPage.subgraph.*` | Subgraph/group node operations |
|
||||
| Vue Nodes | `comfyPage.vueNodes.*` | Requires opt-in: `setSetting('Comfy.VueNodes.Enabled', true)` |
|
||||
| Mouse ops | `comfyPage.page` + `ComfyMouse` | For precise canvas mouse interactions |
|
||||
| Bottom panel | `comfyPage.bottomPanel.*` | Job queue, logs panel |
|
||||
| Commands | `comfyPage.command.*` | Command palette interactions |
|
||||
| Clipboard | `comfyPage.clipboard.*` | Copy/paste operations |
|
||||
| `test('test', async ({ page }) =>` | `test('descriptive-name', async ({ comfyPage }) =>` | Use comfyPage fixture, descriptive names |
|
||||
| `await page.goto('http://...')` | **Remove entirely** | Fixture handles navigation automatically |
|
||||
| `page.locator('canvas')` | `comfyPage.canvas` | Pre-configured canvas locator |
|
||||
| `page.waitForTimeout(N)` | `comfyPage.nextFrame()` | Never use arbitrary waits |
|
||||
| `page.getByPlaceholder('Search Nodes...')` | `comfyPage.searchBox.input` | Use search box page object |
|
||||
| `page` (bare reference) | `comfyPage.page` | Access raw page through fixture |
|
||||
| Bare `test(...)` | `test.describe('Feature', { tag: ['@canvas'] }, () => { test(...) })` | All tests need describe + tags |
|
||||
| No cleanup | Add `test.afterEach(async ({ comfyPage }) => { await comfyPage.canvasOps.resetView() })` | Canvas tests need cleanup |
|
||||
|
||||
## Canvas Coordinates → Node References
|
||||
|
||||
@@ -71,84 +60,16 @@ await comfyPage.searchBox.fillAndSelectFirstNode('KSampler')
|
||||
|
||||
**When to keep coordinates**: Canvas background clicks (pan, zoom), empty area clicks to deselect. These are inherently position-based.
|
||||
|
||||
## Complete Before/After Example
|
||||
|
||||
### Raw codegen output
|
||||
|
||||
```typescript
|
||||
import { test, expect } from '@playwright/test'
|
||||
|
||||
test('test', async ({ page }) => {
|
||||
await page.goto('http://localhost:5173/')
|
||||
await page.locator('canvas').dblclick({ position: { x: 500, y: 400 } })
|
||||
await page.getByPlaceholder('Search Nodes...').fill('KSampler')
|
||||
await page.getByPlaceholder('Search Nodes...').press('Enter')
|
||||
await page.locator('canvas').click({ position: { x: 600, y: 300 } })
|
||||
await page.waitForTimeout(1000)
|
||||
await page.getByRole('button', { name: 'Queue' }).click()
|
||||
})
|
||||
```
|
||||
|
||||
### Convention-compliant output
|
||||
|
||||
```typescript
|
||||
import {
|
||||
comfyPageFixture as test,
|
||||
comfyExpect as expect
|
||||
} from '../fixtures/ComfyPage'
|
||||
|
||||
test.describe('Queue workflow with KSampler', { tag: ['@canvas'] }, () => {
|
||||
test.afterEach(async ({ comfyPage }) => {
|
||||
await comfyPage.canvasOps.resetView()
|
||||
})
|
||||
|
||||
test('should add KSampler node and queue', async ({ comfyPage }) => {
|
||||
// Open search and add KSampler
|
||||
await comfyPage.canvas.dblclick({ position: { x: 500, y: 400 } })
|
||||
await comfyPage.searchBox.fillAndSelectFirstNode('KSampler')
|
||||
await comfyPage.nextFrame()
|
||||
|
||||
// Queue the workflow
|
||||
await comfyPage.menu.topbar.runButton.click()
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
### What changed and why
|
||||
|
||||
1. **Imports**: `@playwright/test` → `../fixtures/ComfyPage` (custom fixtures)
|
||||
2. **Fixture**: `{ page }` → `{ comfyPage }` (access all helpers)
|
||||
3. **goto removed**: Fixture navigates automatically
|
||||
4. **Search box**: Raw locator → `comfyPage.searchBox.fillAndSelectFirstNode()`
|
||||
5. **waitForTimeout**: Replaced with `comfyPage.nextFrame()`
|
||||
6. **Queue button**: Used `comfyPage.menu.topbar.runButton` page object
|
||||
7. **Structure**: Wrapped in `describe` with `@canvas` tag and `afterEach` cleanup
|
||||
8. **Test name**: Generic "test" → descriptive name
|
||||
|
||||
## Decision Guide
|
||||
|
||||
| Question | Answer |
|
||||
| -------------------------- | ------------------------------------------------------------------------------------------- |
|
||||
| Canvas or DOM interaction? | Canvas: `comfyPage.nodeOps.*`. DOM: `comfyPage.vueNodes.*` (needs opt-in) |
|
||||
| Need `nextFrame()`? | Yes after canvas mutations. No after `loadWorkflow()`, no after DOM clicks |
|
||||
| Which tag? | `@canvas` for canvas tests, `@widget` for widget tests, `@screenshot` for visual regression |
|
||||
| Need cleanup? | Yes for canvas tests (`resetView`), yes if changing settings (`setSetting` back) |
|
||||
| Keep pixel coords? | Only for empty canvas clicks. Replace with node refs for node interactions |
|
||||
| Use `page` directly? | Only via `comfyPage.page` for Playwright APIs not wrapped by fixtures |
|
||||
|
||||
## Tags Reference
|
||||
|
||||
| Tag | When to use |
|
||||
| ------------- | ------------------------------------ |
|
||||
| `@canvas` | Any test interacting with the canvas |
|
||||
| `@widget` | Testing widget inputs |
|
||||
| `@smoke` | Quick essential tests |
|
||||
| `@screenshot` | Visual regression (Linux CI only) |
|
||||
| `@mobile` | Mobile viewport (runs on Pixel 5) |
|
||||
| `@2x` | HiDPI tests (2x scale) |
|
||||
| `@0.5x` | Low-DPI tests (0.5x scale) |
|
||||
| `@slow` | Tests taking >10 seconds |
|
||||
| `@perf` | Performance measurement tests |
|
||||
| Question | Answer |
|
||||
| --- | --- |
|
||||
| Canvas or DOM interaction? | Canvas: `comfyPage.nodeOps.*`. DOM: `comfyPage.vueNodes.*` (needs opt-in) |
|
||||
| Need `nextFrame()`? | Yes after canvas mutations. No after `loadWorkflow()`, no after DOM clicks |
|
||||
| Which tag? | `@canvas` for canvas tests, `@widget` for widget tests, `@screenshot` for visual regression |
|
||||
| Need cleanup? | Yes for canvas tests (`resetView`), yes if changing settings (`setSetting` back) |
|
||||
| Keep pixel coords? | Only for empty canvas clicks. Replace with node refs for node interactions |
|
||||
| Use `page` directly? | Only via `comfyPage.page` for Playwright APIs not wrapped by fixtures |
|
||||
|
||||
## Anti-Patterns
|
||||
|
||||
@@ -163,12 +84,12 @@ test.describe('Queue workflow with KSampler', { tag: ['@canvas'] }, () => {
|
||||
|
||||
Read fixture code directly — it's the source of truth:
|
||||
|
||||
| Purpose | Path |
|
||||
| ----------------- | ------------------------------------------ |
|
||||
| Main fixture | `browser_tests/fixtures/ComfyPage.ts` |
|
||||
| Helper classes | `browser_tests/fixtures/helpers/` |
|
||||
| Component objects | `browser_tests/fixtures/components/` |
|
||||
| Test selectors | `browser_tests/fixtures/selectors.ts` |
|
||||
| Vue Node helpers | `browser_tests/fixtures/VueNodeHelpers.ts` |
|
||||
| Existing tests | `browser_tests/tests/` |
|
||||
| Test assets | `browser_tests/assets/` |
|
||||
| Purpose | Path |
|
||||
| --- | --- |
|
||||
| Main fixture | `browser_tests/fixtures/ComfyPage.ts` |
|
||||
| Helper classes | `browser_tests/fixtures/helpers/` |
|
||||
| Component objects | `browser_tests/fixtures/components/` |
|
||||
| Test selectors | `browser_tests/fixtures/selectors.ts` |
|
||||
| Vue Node helpers | `browser_tests/fixtures/VueNodeHelpers.ts` |
|
||||
| Existing tests | `browser_tests/tests/` |
|
||||
| Test assets | `browser_tests/assets/` |
|
||||
|
||||
Reference in New Issue
Block a user