Files
ComfyUI_frontend/browser_tests/tests/dialog.spec.ts
Alexander Brown 5b4ebf4d99 test: audit skipped tests — prune stale, re-enable stable, remove dead code (#10312)
## Summary

Audit all skipped/fixme tests: delete stale tests whose underlying
features were removed, re-enable tests that pass with minimal fixes, and
remove orphaned production code that only the deleted tests exercised.
Net result: **−2,350 lines** across 50 files.

## Changes

- **Pruned stale skipped tests** (entire files deleted):
- `LGraph.configure.test.ts`, `LGraph.constructor.test.ts` — tested
removed LGraph constructor paths
- `LGraphCanvas.ghostAutoPan.test.ts`,
`LGraphCanvas.linkDragAutoPan.test.ts`, `useAutoPan.test.ts`,
`useSlotLinkInteraction.autoPan.test.ts` — tested removed auto-pan
feature
- `useNodePointerInteractions.test.ts` — single skipped test for removed
callback
  - `ImageLightbox.test.ts` — component replaced by `MediaLightbox`
- `appModeWidgetRename.spec.ts` (E2E) — feature removed; helper
`AppModeHelper.ts` also deleted
- `domWidget.spec.ts`, `widget.spec.ts` (E2E) — tested removed widget
behavior

- **Removed orphaned production code** surfaced by test pruning:
- `useAutoPan.ts` — composable + 93 lines of auto-pan logic in
`LGraphCanvas.ts`
  - `ImageLightbox.vue` — replaced by `MediaLightbox`
- Auto-pan integration in `useSlotLinkInteraction.ts` and
`useNodeDrag.ts`
- Dead settings (`LinkSnapping.AutoPanSpeed`,
`LinkSnapping.AutoPanMargin`) in `coreSettings.ts` and
`useLitegraphSettings.ts`
- Unused subgraph methods (`SubgraphNode.getExposedInput`,
`SubgraphInput.getParentInput`)
- Dead i18n key, dead API schema field, dead fixture exports
(`dirtyTest`, `basicSerialisableGraph`)
  - Dead test utility `litegraphTestUtils.ts`

- **Re-enabled skipped tests with minimal fixes**:
  - `useBrowserTabTitle.test.ts` — removed skip, test passes as-is
- `eventUtils.test.ts` — replaced MSW dependency with direct `fetch`
mock
- `SubscriptionPanel.test.ts` — stabilized button selectors,
timezone-safe date assertion
- `LinkConnector.test.ts` — removed stale describe blocks, kept passing
suite
- `widgetUtil.test.ts` — removed skipped tests for deleted functionality
- `comfyManagerStore.test.ts` — removed skipped `isPackInstalling` /
`action buttons` / `loading states` blocks

- **Re-enabled then re-skipped 3 flaky E2E tests** (fail in CI for
pre-existing reasons):
- `browserTabTitle.spec.ts` — canvas click timeout (element not visible)
  - `groupNode.spec.ts` — screenshot diff (stale golden image)
  - `nodeSearchBox.spec.ts` — `p-dialog-mask` intercepts pointer events

- **Simplified production code** alongside test cleanup:
- `useNodeDrag.ts` — removed auto-pan integration, simplified from
170→100 lines
- `DropZone.vue` — refactored URL-drop handling, removed unused code
path
- `ToInputFromIoNodeLink.ts`, `SubgraphInputEventMap.ts` — removed dead
subgraph wiring

- **Dependencies**: none
- **Breaking**: none (all removed code was internal/unused)

## Review Focus

- Confirm deleted production code (`useAutoPan`, `ImageLightbox`,
subgraph methods) has no remaining callers
- Validate that simplified `useNodeDrag.ts` preserves drag behavior
without auto-pan
- Check that re-skipped E2E tests have clear skip reasons for future
triage

## Screenshots (if applicable)

N/A

---------

Co-authored-by: Amp <amp@ampcode.com>
Co-authored-by: github-actions <github-actions@github.com>
2026-03-28 13:08:52 -07:00

444 lines
14 KiB
TypeScript

import { expect } from '@playwright/test'
import type { Keybinding } from '../../src/platform/keybindings/types'
import { comfyPageFixture as test } from '../fixtures/ComfyPage'
import { DefaultGraphPositions } from '../fixtures/constants/defaultGraphPositions'
import { TestIds } from '../fixtures/selectors'
test.beforeEach(async ({ comfyPage }) => {
await comfyPage.settings.setSetting('Comfy.UseNewMenu', 'Disabled')
})
test.describe('Missing nodes in Error Overlay', { tag: '@ui' }, () => {
test.beforeEach(async ({ comfyPage }) => {
await comfyPage.settings.setSetting('Comfy.UseNewMenu', 'Top')
await comfyPage.settings.setSetting(
'Comfy.RightSidePanel.ShowErrorsTab',
true
)
})
test('Should show error overlay when loading a workflow with missing nodes', async ({
comfyPage
}) => {
await comfyPage.workflow.loadWorkflow('missing/missing_nodes')
const errorOverlay = comfyPage.page.getByTestId(
TestIds.dialogs.errorOverlay
)
await expect(errorOverlay).toBeVisible()
const messages = errorOverlay.getByTestId(
TestIds.dialogs.errorOverlayMessages
)
await expect(messages).toBeVisible()
await expect(messages).toHaveText(/missing.*installed/i)
})
test('Should show error overlay when loading a workflow with missing nodes in subgraphs', async ({
comfyPage
}) => {
await comfyPage.workflow.loadWorkflow('missing/missing_nodes_in_subgraph')
const errorOverlay = comfyPage.page.getByTestId(
TestIds.dialogs.errorOverlay
)
await expect(errorOverlay).toBeVisible()
const messages = errorOverlay.getByTestId(
TestIds.dialogs.errorOverlayMessages
)
await expect(messages).toBeVisible()
await expect(messages).toHaveText(/missing.*installed/i)
// Click "See Errors" to open the errors tab and verify subgraph node content
await errorOverlay
.getByTestId(TestIds.dialogs.errorOverlaySeeErrors)
.click()
await expect(errorOverlay).not.toBeVisible()
const missingNodeCard = comfyPage.page.getByTestId(
TestIds.dialogs.missingNodeCard
)
await expect(missingNodeCard).toBeVisible()
// Expand the pack group row to reveal node type names
await missingNodeCard
.getByRole('button', { name: /expand/i })
.first()
.click()
await expect(
missingNodeCard.getByText('MISSING_NODE_TYPE_IN_SUBGRAPH')
).toBeVisible()
})
test('Should show MissingNodeCard in errors tab when clicking See Errors', async ({
comfyPage
}) => {
await comfyPage.workflow.loadWorkflow('missing/missing_nodes')
const errorOverlay = comfyPage.page.getByTestId(
TestIds.dialogs.errorOverlay
)
await expect(errorOverlay).toBeVisible()
// Click "See Errors" to open the right side panel errors tab
await errorOverlay
.getByTestId(TestIds.dialogs.errorOverlaySeeErrors)
.click()
await expect(errorOverlay).not.toBeVisible()
// Verify MissingNodeCard is rendered in the errors tab
const missingNodeCard = comfyPage.page.getByTestId(
TestIds.dialogs.missingNodeCard
)
await expect(missingNodeCard).toBeVisible()
})
})
test('Does not resurface missing nodes on undo/redo', async ({ comfyPage }) => {
await comfyPage.settings.setSetting('Comfy.UseNewMenu', 'Top')
await comfyPage.settings.setSetting(
'Comfy.RightSidePanel.ShowErrorsTab',
true
)
await comfyPage.workflow.loadWorkflow('missing/missing_nodes')
const errorOverlay = comfyPage.page.getByTestId(TestIds.dialogs.errorOverlay)
await expect(errorOverlay).toBeVisible()
// Dismiss the error overlay
await errorOverlay.getByTestId(TestIds.dialogs.errorOverlayDismiss).click()
await expect(errorOverlay).not.toBeVisible()
// Make a change to the graph by moving a node
await comfyPage.canvas.click()
await comfyPage.nextFrame()
await comfyPage.page.keyboard.press('Control+a')
await comfyPage.page.mouse.move(400, 300)
await comfyPage.page.mouse.down()
await comfyPage.page.mouse.move(450, 350, { steps: 5 })
await comfyPage.page.mouse.up()
await comfyPage.nextFrame()
// Undo and redo should not resurface the error overlay
await comfyPage.keyboard.undo()
await expect(errorOverlay).not.toBeVisible({ timeout: 5000 })
await comfyPage.keyboard.redo()
await expect(errorOverlay).not.toBeVisible({ timeout: 5000 })
})
test.describe('Execution error', () => {
test.beforeEach(async ({ comfyPage }) => {
await comfyPage.settings.setSetting('Comfy.UseNewMenu', 'Top')
await comfyPage.settings.setSetting(
'Comfy.RightSidePanel.ShowErrorsTab',
true
)
await comfyPage.setup()
})
test('Should display an error message when an execution error occurs', async ({
comfyPage
}) => {
await comfyPage.workflow.loadWorkflow('nodes/execution_error')
await comfyPage.command.executeCommand('Comfy.QueuePrompt')
await comfyPage.nextFrame()
// Wait for the error overlay to be visible
const errorOverlay = comfyPage.page.getByTestId(
TestIds.dialogs.errorOverlay
)
await expect(errorOverlay).toBeVisible()
})
})
test.describe('Error actions in Errors Tab', { tag: '@ui' }, () => {
test.beforeEach(async ({ comfyPage }) => {
await comfyPage.settings.setSetting('Comfy.UseNewMenu', 'Top')
await comfyPage.settings.setSetting(
'Comfy.RightSidePanel.ShowErrorsTab',
true
)
})
test('Should show Find on GitHub and Copy buttons in error card after execution error', async ({
comfyPage
}) => {
await comfyPage.workflow.loadWorkflow('nodes/execution_error')
await comfyPage.command.executeCommand('Comfy.QueuePrompt')
await comfyPage.nextFrame()
// Wait for error overlay and click "See Errors"
const errorOverlay = comfyPage.page.getByTestId(
TestIds.dialogs.errorOverlay
)
await expect(errorOverlay).toBeVisible()
await errorOverlay
.getByTestId(TestIds.dialogs.errorOverlaySeeErrors)
.click()
await expect(errorOverlay).not.toBeVisible()
// Verify Find on GitHub button is present in the error card
const findOnGithubButton = comfyPage.page.getByTestId(
TestIds.dialogs.errorCardFindOnGithub
)
await expect(findOnGithubButton).toBeVisible()
// Verify Copy button is present in the error card
const copyButton = comfyPage.page.getByTestId(TestIds.dialogs.errorCardCopy)
await expect(copyButton).toBeVisible()
})
})
test.describe('Missing models in Error Tab', () => {
test.beforeEach(async ({ comfyPage }) => {
await comfyPage.settings.setSetting('Comfy.UseNewMenu', 'Top')
await comfyPage.settings.setSetting(
'Comfy.RightSidePanel.ShowErrorsTab',
true
)
const cleanupOk = await comfyPage.page.evaluate(async (url: string) => {
const response = await fetch(`${url}/api/devtools/cleanup_fake_model`)
return response.ok
}, comfyPage.url)
expect(cleanupOk).toBeTruthy()
})
test('Should show error overlay with missing models when workflow has missing models', async ({
comfyPage
}) => {
await comfyPage.workflow.loadWorkflow('missing/missing_models')
const errorOverlay = comfyPage.page.getByTestId(
TestIds.dialogs.errorOverlay
)
await expect(errorOverlay).toBeVisible()
const messages = errorOverlay.getByTestId(
TestIds.dialogs.errorOverlayMessages
)
await expect(messages).toBeVisible()
await expect(messages).toHaveText(/required model.*missing/i)
})
test('Should show missing models from node properties', async ({
comfyPage
}) => {
await comfyPage.workflow.loadWorkflow(
'missing/missing_models_from_node_properties'
)
const errorOverlay = comfyPage.page.getByTestId(
TestIds.dialogs.errorOverlay
)
await expect(errorOverlay).toBeVisible()
const messages = errorOverlay.getByTestId(
TestIds.dialogs.errorOverlayMessages
)
await expect(messages).toBeVisible()
await expect(messages).toHaveText(/required model.*missing/i)
})
test('Should not show missing models when widget values have changed', async ({
comfyPage
}) => {
await comfyPage.workflow.loadWorkflow(
'missing/model_metadata_widget_mismatch'
)
await expect(
comfyPage.page.getByTestId(TestIds.dialogs.errorOverlay)
).not.toBeVisible()
await expect(
comfyPage.page.getByTestId(TestIds.dialogs.errorOverlayMessages)
).not.toBeVisible()
})
})
test.describe('Settings', () => {
test('@mobile Should be visible on mobile', async ({ comfyPage }) => {
await comfyPage.page.keyboard.press('Control+,')
const settingsDialog = comfyPage.page.locator(
'[data-testid="settings-dialog"]'
)
await expect(settingsDialog).toBeVisible()
const contentArea = settingsDialog.locator('main')
await expect(contentArea).toBeVisible()
const isUsableHeight = await contentArea.evaluate(
(el) => el.clientHeight > 30
)
expect(isUsableHeight).toBeTruthy()
})
test('Can open settings with hotkey', async ({ comfyPage }) => {
await comfyPage.page.keyboard.down('ControlOrMeta')
await comfyPage.page.keyboard.press(',')
await comfyPage.page.keyboard.up('ControlOrMeta')
const settingsLocator = comfyPage.page.locator(
'[data-testid="settings-dialog"]'
)
await expect(settingsLocator).toBeVisible()
await comfyPage.page.keyboard.press('Escape')
await expect(settingsLocator).not.toBeVisible()
})
test('Can change canvas zoom speed setting', async ({ comfyPage }) => {
const maxSpeed = 2.5
await comfyPage.settings.setSetting('Comfy.Graph.ZoomSpeed', maxSpeed)
await test.step('Setting should persist', async () => {
expect(await comfyPage.settings.getSetting('Comfy.Graph.ZoomSpeed')).toBe(
maxSpeed
)
})
})
test('Should persist keybinding setting', async ({ comfyPage }) => {
// Open the settings dialog
await comfyPage.page.keyboard.press('Control+,')
await comfyPage.page.waitForSelector('[data-testid="settings-dialog"]')
// Open the keybinding tab
const settingsDialog = comfyPage.page.locator(
'[data-testid="settings-dialog"]'
)
await settingsDialog
.locator('nav [role="button"]', { hasText: 'Keybinding' })
.click()
await comfyPage.page.waitForSelector(
'[placeholder="Search Keybindings..."]'
)
// Focus the 'New Blank Workflow' row
const newBlankWorkflowRow = comfyPage.page.locator('tr', {
has: comfyPage.page.getByRole('cell', { name: 'New Blank Workflow' })
})
await newBlankWorkflowRow.click()
// Click add keybinding button (New Blank Workflow has no default keybinding)
const addKeybindingButton = newBlankWorkflowRow.locator(
'.icon-\\[lucide--plus\\]'
)
await addKeybindingButton.click()
// Set new keybinding
const input = comfyPage.page.getByPlaceholder('Enter your keybind')
await input.press('Alt+n')
const requestPromise = comfyPage.page.waitForRequest(
(req) =>
req.url().includes('/api/settings') &&
!req.url().includes('/api/settings/') &&
req.method() === 'POST'
)
// Save keybinding
const saveButton = comfyPage.page
.getByLabel('Modify keybinding')
.getByText('Save')
await saveButton.click()
const request = await requestPromise
const expectedSetting: Keybinding = {
commandId: 'Comfy.NewBlankWorkflow',
combo: {
key: 'n',
ctrl: false,
alt: true,
shift: false
}
}
expect(request.postData()).toContain(JSON.stringify(expectedSetting))
})
})
test.describe('Support', () => {
test('Should open external zendesk link with OSS tag', async ({
comfyPage
}) => {
await comfyPage.settings.setSetting('Comfy.UseNewMenu', 'Top')
// Prevent loading the external page
await comfyPage.page
.context()
.route('https://support.comfy.org/**', (route) =>
route.fulfill({ body: '<html></html>', contentType: 'text/html' })
)
const popupPromise = comfyPage.page.waitForEvent('popup')
await comfyPage.menu.topbar.triggerTopbarCommand(['Help', 'Support'])
const popup = await popupPromise
const url = new URL(popup.url())
expect(url.hostname).toBe('support.comfy.org')
expect(url.searchParams.get('tf_42243568391700')).toBe('oss')
await popup.close()
})
})
test.describe('Error dialog', () => {
test('Should display an error dialog when graph configure fails', async ({
comfyPage
}) => {
await comfyPage.page.evaluate(() => {
const graph = window.graph!
;(graph as { configure: () => void }).configure = () => {
throw new Error('Error on configure!')
}
})
await comfyPage.workflow.loadWorkflow('default')
const errorDialog = comfyPage.page.locator('.comfy-error-report')
await expect(errorDialog).toBeVisible()
})
test('Should display an error dialog when prompt execution fails', async ({
comfyPage
}) => {
await comfyPage.page.evaluate(async () => {
const app = window.app!
app.api.queuePrompt = () => {
throw new Error('Error on queuePrompt!')
}
await app.queuePrompt(0)
})
const errorDialog = comfyPage.page.locator('.comfy-error-report')
await expect(errorDialog).toBeVisible()
})
})
test.describe('Signin dialog', () => {
test('Paste content to signin dialog should not paste node on canvas', async ({
comfyPage
}) => {
const nodeNum = await comfyPage.nodeOps.getNodeCount()
await comfyPage.canvas.click({
position: DefaultGraphPositions.emptyLatentWidgetClick
})
await comfyPage.page.mouse.move(10, 10)
await comfyPage.nextFrame()
await comfyPage.clipboard.copy()
const textBox = comfyPage.widgetTextBox
await textBox.click()
await textBox.fill('test_password')
await textBox.press('Control+a')
await textBox.press('Control+c')
await comfyPage.page.evaluate(() => {
void window.app!.extensionManager.dialog.showSignInDialog()
})
const input = comfyPage.page.locator('#comfy-org-sign-in-password')
await input.waitFor({ state: 'visible' })
await input.press('Control+v')
await expect(input).toHaveValue('test_password')
expect(await comfyPage.nodeOps.getNodeCount()).toBe(nodeNum)
})
})