Files
ComfyUI_frontend/docs/guidance/playwright.md
Christian Byrne b49ea9fabd feat: add getNodesByTitle and getNodeByTitleNth helpers to VueNodeHelpers (#10666)
## Summary

Add helpers for safely interacting with nodes that share the same title
without hitting Playwright strict mode.

## Changes

- **What**: Added `getNodesByTitle(title)` and `getNodeByTitleNth(title,
index)` to `VueNodeHelpers`. Updated `docs/guidance/playwright.md` with
a gotcha note about duplicate node names.

## Review Focus

These are purely additive helpers — no existing behavior changes.
`getNodesByTitle` returns all matching nodes (callers use `.nth()` to
pick), and `getNodeByTitleNth` is a convenience wrapper. The existing
`selectNodes(nodeIds)` by-ID method is unchanged.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10666-feat-add-getNodesByTitle-and-getNodeByTitleNth-helpers-to-VueNodeHelpers-3316d73d3650812eabe6e56a768a34d2)
by [Unito](https://www.unito.io)
2026-03-28 16:09:18 -07:00

3.8 KiB

globs
globs
**/*.spec.ts

Playwright E2E Test Conventions

See docs/testing/*.md for detailed patterns.

Best Practices

  • Follow Playwright Best Practices
  • Do NOT use waitForTimeout - use Locator actions and retrying assertions
  • Prefer specific selectors (role, label, test-id)
  • Test across viewports

Window Globals

Browser tests access window.app, window.graph, and window.LiteGraph which are optional in the main app types. In E2E tests, use non-null assertions (!):

window.app!.graph!.nodes
window.LiteGraph!.registered_node_types

This is the only context where non-null assertions are acceptable.

TODO: Consolidate these references into a central utility (e.g., getApp()) that performs proper runtime type checking, removing the need for scattered ! assertions.

Type Assertions in E2E Tests

E2E tests may use specific type assertions when needed, but never as any.

Acceptable Patterns

// ✅ Non-null assertions for window globals
window.app!.extensionManager

// ✅ Specific type assertions with documentation
// Extensions can register arbitrary setting IDs
id: 'TestSetting' as TestSettingId

// ✅ Test-local type helpers
type TestSettingId = keyof Settings

Forbidden Patterns

// ❌ Never use `as any`
settings: testData as any

// ❌ Never modify production types to satisfy test errors
// Don't add test settings to src/schemas/apiSchema.ts

// ❌ Don't chain through unknown to bypass types
data as unknown as SomeType // Avoid; prefer `as Partial<SomeType> as SomeType` or explicit typings

Accessing Internal State

When tests need internal store properties (e.g., .workflow, .focusMode):

// ✅ Access stores directly in page.evaluate
await page.evaluate(() => {
  const store = useWorkflowStore()
  return store.activeWorkflow
})

// ❌ Don't change public API types to expose internals
// Keep app.extensionManager typed as ExtensionManager, not WorkspaceStore

Assertion Best Practices

When a test depends on an invariant unrelated to what it's actually testing (e.g. asserting a node has 4 widgets before testing node movement), always assert that invariant explicitly — don't leave it unchecked. Use a custom message or expect.soft() rather than a bare expect, so failures point to the broken assumption instead of producing a confusing error downstream.

// ✅ Custom message on an unrelated precondition — clear signal when the invariant breaks
expect(node.widgets, 'Widget count changed — update test fixture').toHaveLength(
  4
)
await node.move(100, 200)

// ✅ Soft assertion — verifies multiple invariants without stopping the test early
expect.soft(menuItem1).toBeVisible()
expect.soft(menuItem2).toBeVisible()
expect.soft(menuItem3).toBeVisible()

// ❌ Bare expect on a precondition — no context when it fails
expect(node.widgets).toHaveLength(4)
  • Use custom messages (expect(x, 'reason')) for precondition checks unrelated to the test's purpose
  • Use expect.soft() when you want to verify multiple invariants without aborting on the first failure
  • Prefer Playwright's built-in message parameter over custom error classes

Test Tags

Tags are respected by config:

  • @mobile - Mobile viewport tests
  • @2x - High DPI tests

Test Data

  • Check browser_tests/assets/ for test data and fixtures
  • Use realistic ComfyUI workflows for E2E tests
  • When multiple nodes share the same title (e.g. two "CLIP Text Encode" nodes), use vueNodes.getNodeByTitle(name).nth(n) to pick a specific one. Never interact with the bare locator when titles are non-unique — Playwright strict mode will fail.

Running Tests

pnpm test:browser:local                 # Run all E2E tests
pnpm test:browser:local -- --ui         # Interactive UI mode