---
name: codegen-transform
description: '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.'
---

# 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:

```typescript
// ❌ 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.*`. Vue node DOM: `comfyPage.vueNodes.*` with `@vue-nodes`                                         |
| Need `nextFrame()`?        | Yes after canvas mutations. No after `loadWorkflow()`, no after DOM clicks                                                    |
| Which tag?                 | Always add `@vue-nodes` when the test needs Vue Nodes, alongside scenario tags such as `@canvas`, `@widget`, or `@screenshot` |
| Starting settings?         | Prefer `test.use({ initialSettings })` at the narrowest file or describe scope instead of `setSetting` in `beforeEach`        |
| Need cleanup?              | Yes for canvas tests (`resetView`). No settings resets: the fixture replaces settings with a baseline before each test        |
| 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                                                         |

The `@vue-nodes` tag enables the renderer before boot and makes the fixture wait
for nodes. Never manually set `Comfy.VueNodes.Enabled`, including through
`initialSettings`, or call `comfyPage.vueNodes.waitForNodes()` in tests.
For tests of renderer switching, use the menu or command helpers described in
[the Vue Nodes guide](../../../browser_tests/README.md#vue-nodes-vs-litegraph--decision-guide).

Nested `test.use` calls replace the parent's `initialSettings` object, so include
all overrides needed by the nested scope. Keep runtime `setSetting` calls only
when a live change or persistence is the behavior under test. See
[starting settings and isolation](../../../browser_tests/README.md#starting-settings-and-isolation)
for baseline and manual-boot rules.

## Anti-Patterns

1. **Never use `waitForTimeout`** → use `nextFrame()` or retrying assertions
2. **Never use `page.goto`** → fixture handles navigation
3. **Never import from `@playwright/test`** → use `@e2e/fixtures/ComfyPage`
4. **Never use bare CSS selectors** → use test IDs or semantic locators
5. **Never share state between tests** → each test is independent
6. **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/`                    |
