Codegen → Convention Transform
Transform raw Playwright codegen output into tests that follow ComfyUI conventions.
When to Use
- QA tester recorded a test with
pnpm comfy-test record and wants refinement
- Developer pasted raw
npx playwright codegen output
- 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
The programmatic transform engine lives in tools/test-recorder/src/transform/rules.ts. Apply these replacements in order:
| Raw codegen |
Convention replacement |
Why |
import { test, expect } from '@playwright/test' |
import { comfyPageFixture as test, comfyExpect as expect } from '@e2e/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: ['<scenario tag>'] }, () => { 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
Raw codegen records fragile pixel coordinates. Replace with node references when possible:
// ❌ Raw codegen — fragile pixel coordinates
await page.locator('canvas').click({ position: { x: 423, y: 267 } })
// ✅ If clicking a specific node
const node = (await comfyPage.nodeOps.getNodeRefsByType('KSampler'))[0]
await node.click('title')
// ✅ If double-clicking canvas to open search
await comfyPage.canvas.dblclick({ position: { x: 500, y: 400 } })
await comfyPage.searchBox.fillAndSelectFirstNode('KSampler')
When to keep coordinates: Canvas background clicks (pan, zoom), empty area clicks to deselect. These are inherently position-based.
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 |
Anti-Patterns
- Never use
waitForTimeout → use nextFrame() or retrying assertions
- Never use
page.goto → fixture handles navigation
- Never import from
@playwright/test → use @e2e/fixtures/ComfyPage
- Never use bare CSS selectors → use test IDs or semantic locators
- Never share state between tests → each test is independent
- Never commit local screenshots → Linux CI generates baselines
For Deeper Reference
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/ |
1---2name: codegen-transform3description: Transforms raw Playwright codegen output into ComfyUI convention-compliant tests. Use when: user pastes raw codegen, asks to convert raw Playwright code, refactor recorded tests, or rewrite to project conventions. Triggers on: transform codegen, convert raw test, rewrite to conventions, codegen output, raw playwright.4---56# Codegen → Convention Transform78Transform raw Playwright codegen output into tests that follow ComfyUI conventions.910## When to Use1112- QA tester recorded a test with `pnpm comfy-test record` and wants refinement13- Developer pasted raw `npx playwright codegen` output14- Agent needs to post-process Playwright test agent output15- Reviewing a test that uses raw `page.*` calls instead of fixture helpers1617## Reference Documentation1819Before transforming, read these existing docs for full context:2021| Document | What it covers |22| ------------------------------------- | ----------------------------------------------------------------------- |23| `docs/guidance/playwright.md` | Playwright conventions, type assertions, assertion best practices, tags |24| `browser_tests/AGENTS.md` | Directory structure, polling assertions, gotchas, quality checks |25| `browser_tests/fixtures/ComfyPage.ts` | Main fixture API (source of truth for all helpers) |26| `browser_tests/fixtures/helpers/` | Focused helper classes (canvas, keyboard, workflow, etc.) |2728## Transform Rules2930The programmatic transform engine lives in `tools/test-recorder/src/transform/rules.ts`. Apply these replacements in order:3132| Raw codegen | Convention replacement | Why |33| ------------------------------------------------- | ------------------------------------------------------------------------------------------- | ---------------------------------------- |34| `import { test, expect } from '@playwright/test'` | `import { comfyPageFixture as test, comfyExpect as expect } from '@e2e/fixtures/ComfyPage'` | Use custom fixtures with ComfyUI helpers |35| `test('test', async ({ page }) =>` | `test('descriptive-name', async ({ comfyPage }) =>` | Use comfyPage fixture, descriptive names |36| `await page.goto('http://...')` | **Remove entirely** | Fixture handles navigation automatically |37| `page.locator('canvas')` | `comfyPage.canvas` | Pre-configured canvas locator |38| `page.waitForTimeout(N)` | `comfyPage.nextFrame()` | Never use arbitrary waits |39| `page.getByPlaceholder('Search Nodes...')` | `comfyPage.searchBox.input` | Use search box page object |40| `page` (bare reference) | `comfyPage.page` | Access raw page through fixture |41| Bare `test(...)` | `test.describe('Feature', { tag: ['<scenario tag>'] }, () => { test(...) })` | All tests need describe + tags |42| No cleanup | Add `test.afterEach(async ({ comfyPage }) => { await comfyPage.canvasOps.resetView() })` | Canvas tests need cleanup |4344## Canvas Coordinates → Node References4546Raw codegen records fragile pixel coordinates. Replace with node references when possible:4748```typescript49// ❌ Raw codegen — fragile pixel coordinates50await page.locator('canvas').click({ position: { x: 423, y: 267 } })5152// ✅ If clicking a specific node53const node = (await comfyPage.nodeOps.getNodeRefsByType('KSampler'))[0]54await node.click('title')5556// ✅ If double-clicking canvas to open search57await comfyPage.canvas.dblclick({ position: { x: 500, y: 400 } })58await comfyPage.searchBox.fillAndSelectFirstNode('KSampler')59```6061**When to keep coordinates**: Canvas background clicks (pan, zoom), empty area clicks to deselect. These are inherently position-based.6263## Decision Guide6465| Question | Answer |66| -------------------------- | ------------------------------------------------------------------------------------------- |67| Canvas or DOM interaction? | Canvas: `comfyPage.nodeOps.*`. DOM: `comfyPage.vueNodes.*` (needs opt-in) |68| Need `nextFrame()`? | Yes after canvas mutations. No after `loadWorkflow()`, no after DOM clicks |69| Which tag? | `@canvas` for canvas tests, `@widget` for widget tests, `@screenshot` for visual regression |70| Need cleanup? | Yes for canvas tests (`resetView`), yes if changing settings (`setSetting` back) |71| Keep pixel coords? | Only for empty canvas clicks. Replace with node refs for node interactions |72| Use `page` directly? | Only via `comfyPage.page` for Playwright APIs not wrapped by fixtures |7374## Anti-Patterns75761. **Never use `waitForTimeout`** → use `nextFrame()` or retrying assertions772. **Never use `page.goto`** → fixture handles navigation783. **Never import from `@playwright/test`** → use `@e2e/fixtures/ComfyPage`794. **Never use bare CSS selectors** → use test IDs or semantic locators805. **Never share state between tests** → each test is independent816. **Never commit local screenshots** → Linux CI generates baselines8283## For Deeper Reference8485Read fixture code directly — it's the source of truth:8687| Purpose | Path |88| ----------------- | ------------------------------------------ |89| Main fixture | `browser_tests/fixtures/ComfyPage.ts` |90| Helper classes | `browser_tests/fixtures/helpers/` |91| Component objects | `browser_tests/fixtures/components/` |92| Test selectors | `browser_tests/fixtures/selectors.ts` |93| Vue Node helpers | `browser_tests/fixtures/VueNodeHelpers.ts` |94| Existing tests | `browser_tests/tests/` |95| Test assets | `browser_tests/assets/` |