Files
ComfyUI_frontend/.claude/skills/writing-playwright-tests/testing/widgets.md
2026-02-03 16:54:38 -08:00

4.2 KiB

Widget Patterns

⚠️ LiteGraph Mode: These patterns apply to the default LiteGraph canvas rendering where widgets are drawn on canvas. For Vue Nodes 2.0 (DOM-based widgets), see vue-nodes.md.

Mode Widget Access Interaction Style
LiteGraph node.getWidget('seed') setValue(), getValue(), canvas coordinates
Vue Nodes comfyPage.vueNodes.getWidgetByName() DOM locators, click(), fill()

Getting Widgets

const node = comfyPage.getNodeRefByTitle('KSampler')
const widget = node.getWidget('seed')

Widget Types

Number Widgets (INT, FLOAT)

const seedWidget = node.getWidget('seed')
await seedWidget.setValue(12345)
const value = await seedWidget.getValue()

Combo/Dropdown Widgets

const samplerWidget = node.getWidget('sampler_name')
await samplerWidget.setValue('euler')

// Or click to open dropdown
await samplerWidget.click()
await comfyPage.page.getByText('euler_ancestral').click()
await comfyPage.nextFrame()

String/Text Widgets

const promptWidget = node.getWidget('text')
await promptWidget.setValue('a beautiful landscape')

Toggle/Boolean Widgets

const toggleWidget = node.getWidget('enable')
await toggleWidget.setValue(true)
// or
await toggleWidget.click()
await comfyPage.nextFrame()

Slider Widgets

const sliderWidget = node.getWidget('denoise')
await sliderWidget.setValue(0.75)

Widget Value Assertions

const widget = node.getWidget('steps')
const value = await widget.getValue()
expect(value).toBe(20)

Widget Visibility

// Check widget is visible
await expect(widget.locator).toBeVisible()

// Widget might be hidden when node is collapsed
await node.expand()
await comfyPage.nextFrame()
await expect(widget.locator).toBeVisible()

Common Widget Gotchas

1. Wait for Value Change

Widget values may not update instantly. Use retry patterns:

await widget.setValue(100)
await comfyPage.nextFrame()

// Use poll for single value
await expect.poll(() => widget.getValue(), { timeout: 2000 }).toBe(100)

See debugging.md for more retry patterns.

2. Combo Widget Selection

Click-based selection is more reliable than setValue for combos:

await samplerWidget.click()
await comfyPage.page.getByRole('option', { name: 'euler' }).click()
await comfyPage.nextFrame()

3. Widget Focus

Some widgets need focus before input:

await widget.locator.click()
await widget.locator.fill('new value')
await comfyPage.nextFrame()

Example: Complete Widget Test

import {
  comfyPageFixture as test,
  comfyExpect as expect
} from './fixtures/ComfyPage'

test.describe('Widget Operations', { tag: ['@widget'] }, () => {
  test.beforeEach(async ({ comfyPage }) => {
    await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled')
    await comfyPage.loadWorkflow('widgets/all_types')
    await comfyPage.nextFrame()
  })

  test('sets number widget value', async ({ comfyPage }) => {
    const node = comfyPage.getNodeRefByTitle('KSampler')
    const seedWidget = node.getWidget('seed')

    await seedWidget.setValue(42)
    await comfyPage.nextFrame()

    const value = await seedWidget.getValue()
    expect(value).toBe(42)
  })

  test('selects combo option', async ({ comfyPage }) => {
    const node = comfyPage.getNodeRefByTitle('KSampler')
    const samplerWidget = node.getWidget('sampler_name')

    await samplerWidget.click()
    await comfyPage.page.getByRole('option', { name: 'dpmpp_2m' }).click()
    await comfyPage.nextFrame()

    const value = await samplerWidget.getValue()
    expect(value).toBe('dpmpp_2m')
  })
})

Asset Workflows for Widget Testing

Pre-made workflows for widget tests:

  • assets/widgets/combo_widget.json
  • assets/widgets/slider_widget.json
  • assets/widgets/text_widget.json
  • assets/widgets/number_widget.json