mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-20 06:20:11 +00:00
## 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>
353 lines
12 KiB
TypeScript
353 lines
12 KiB
TypeScript
import {
|
|
comfyExpect as expect,
|
|
comfyPageFixture as test
|
|
} from '../fixtures/ComfyPage'
|
|
import type { ComfyPage } from '../fixtures/ComfyPage'
|
|
|
|
test.beforeEach(async ({ comfyPage }) => {
|
|
await comfyPage.settings.setSetting('Comfy.UseNewMenu', 'Disabled')
|
|
})
|
|
|
|
test.describe('Node search box', { tag: '@node' }, () => {
|
|
test.beforeEach(async ({ comfyPage }) => {
|
|
await comfyPage.settings.setSetting(
|
|
'Comfy.LinkRelease.Action',
|
|
'search box'
|
|
)
|
|
await comfyPage.settings.setSetting(
|
|
'Comfy.LinkRelease.ActionShift',
|
|
'search box'
|
|
)
|
|
await comfyPage.settings.setSetting(
|
|
'Comfy.NodeSearchBoxImpl',
|
|
'v1 (legacy)'
|
|
)
|
|
})
|
|
|
|
test(`Can trigger on empty canvas double click`, async ({ comfyPage }) => {
|
|
await comfyPage.canvasOps.doubleClick()
|
|
await expect(comfyPage.searchBox.input).toHaveCount(1)
|
|
})
|
|
|
|
test(`Can trigger on group body double click`, async ({ comfyPage }) => {
|
|
await comfyPage.workflow.loadWorkflow('groups/single_group_only')
|
|
await comfyPage.page.mouse.dblclick(50, 50, { delay: 5 })
|
|
await comfyPage.nextFrame()
|
|
await expect(comfyPage.searchBox.input).toHaveCount(1)
|
|
})
|
|
|
|
test('Can trigger on link release', async ({ comfyPage }) => {
|
|
await comfyPage.canvasOps.disconnectEdge()
|
|
await expect(comfyPage.searchBox.input).toHaveCount(1)
|
|
})
|
|
|
|
test('New user (1.24.1+) gets search box by default on link release', async ({
|
|
comfyPage
|
|
}) => {
|
|
// Start fresh to test new user behavior
|
|
await comfyPage.setup({ clearStorage: true })
|
|
// Simulate new user with 1.24.1+ installed version
|
|
await comfyPage.settings.setSetting('Comfy.InstalledVersion', '1.24.1')
|
|
await comfyPage.settings.setSetting(
|
|
'Comfy.NodeSearchBoxImpl',
|
|
'v1 (legacy)'
|
|
)
|
|
// Don't set LinkRelease settings explicitly to test versioned defaults
|
|
|
|
await comfyPage.canvasOps.disconnectEdge()
|
|
await expect(comfyPage.searchBox.input).toHaveCount(1)
|
|
await expect(comfyPage.searchBox.input).toBeVisible()
|
|
})
|
|
|
|
test('Can add node', { tag: '@screenshot' }, async ({ comfyPage }) => {
|
|
await comfyPage.canvasOps.doubleClick()
|
|
await expect(comfyPage.searchBox.input).toHaveCount(1)
|
|
await comfyPage.searchBox.fillAndSelectFirstNode('KSampler')
|
|
await expect(comfyPage.canvas).toHaveScreenshot('added-node.png')
|
|
})
|
|
|
|
test('Can auto link node', { tag: '@screenshot' }, async ({ comfyPage }) => {
|
|
await comfyPage.canvasOps.disconnectEdge()
|
|
// Select the second item as the first item is always reroute
|
|
await comfyPage.searchBox.fillAndSelectFirstNode('CLIPTextEncode', {
|
|
suggestionIndex: 0
|
|
})
|
|
await expect(comfyPage.canvas).toHaveScreenshot('auto-linked-node.png')
|
|
})
|
|
|
|
test(
|
|
'Can auto link batch moved node',
|
|
{ tag: '@screenshot' },
|
|
async ({ comfyPage }) => {
|
|
await comfyPage.settings.setSetting('Comfy.Graph.AutoPanSpeed', 0)
|
|
await comfyPage.workflow.loadWorkflow('links/batch_move_links')
|
|
|
|
// Get the CLIP output slot (index 1) from the first CheckpointLoaderSimple node (id: 4)
|
|
const checkpointNode = await comfyPage.nodeOps.getNodeRefById(4)
|
|
const clipOutputSlot = await checkpointNode.getOutput(1)
|
|
const outputSlotPos = await clipOutputSlot.getPosition()
|
|
|
|
// Use a position in the empty canvas area (top-left corner)
|
|
const emptySpacePos = { x: 5, y: 5 }
|
|
|
|
await comfyPage.page.keyboard.down('Shift')
|
|
await comfyPage.canvasOps.dragAndDrop(outputSlotPos, emptySpacePos)
|
|
await comfyPage.page.keyboard.up('Shift')
|
|
|
|
// Select the second item as the first item is always reroute
|
|
await comfyPage.searchBox.fillAndSelectFirstNode('Load Checkpoint', {
|
|
suggestionIndex: 0
|
|
})
|
|
await expect(comfyPage.canvas).toHaveScreenshot(
|
|
'auto-linked-node-batch.png'
|
|
)
|
|
}
|
|
)
|
|
|
|
test(
|
|
'Link release connecting to node with no slots',
|
|
{ tag: '@screenshot' },
|
|
async ({ comfyPage }) => {
|
|
await comfyPage.canvasOps.disconnectEdge()
|
|
await expect(comfyPage.searchBox.input).toHaveCount(1)
|
|
await comfyPage.page.locator('.p-chip-remove-icon').click()
|
|
await comfyPage.searchBox.fillAndSelectFirstNode('KSampler', {
|
|
exact: true
|
|
})
|
|
await expect(comfyPage.canvas).toHaveScreenshot(
|
|
'added-node-no-connection.png'
|
|
)
|
|
}
|
|
)
|
|
|
|
test('Has correct aria-labels on search results', async ({ comfyPage }) => {
|
|
const node = 'Load Checkpoint'
|
|
await comfyPage.canvasOps.doubleClick()
|
|
await comfyPage.searchBox.input.waitFor({ state: 'visible' })
|
|
await comfyPage.searchBox.input.fill(node)
|
|
await comfyPage.searchBox.dropdown.waitFor({ state: 'visible' })
|
|
|
|
const firstResult = comfyPage.searchBox.dropdown.locator('li').first()
|
|
await expect(firstResult).toHaveAttribute('aria-label', node)
|
|
})
|
|
|
|
test('@mobile Can trigger on empty canvas tap', async ({ comfyPage }) => {
|
|
await comfyPage.closeMenu()
|
|
await comfyPage.workflow.loadWorkflow('nodes/single_ksampler')
|
|
const screenCenter = {
|
|
x: 200,
|
|
y: 400
|
|
}
|
|
await comfyPage.canvas.tap({
|
|
position: screenCenter
|
|
})
|
|
await comfyPage.canvas.tap({
|
|
position: screenCenter
|
|
})
|
|
await expect(comfyPage.searchBox.input).not.toHaveCount(0)
|
|
})
|
|
|
|
test.describe('Filtering', () => {
|
|
const expectFilterChips = async (
|
|
comfyPage: ComfyPage,
|
|
expectedTexts: string[]
|
|
) => {
|
|
const chips = comfyPage.searchBox.filterChips
|
|
|
|
// Check that the number of chips matches the expected count
|
|
await expect(chips).toHaveCount(expectedTexts.length)
|
|
|
|
// Verify the text and visibility of each filter chip
|
|
await Promise.all(
|
|
expectedTexts.map(async (text, index) => {
|
|
const chip = chips.nth(index)
|
|
await expect(chip).toContainText(text)
|
|
await expect(chip).toBeVisible()
|
|
})
|
|
)
|
|
}
|
|
|
|
test.beforeEach(async ({ comfyPage }) => {
|
|
await comfyPage.canvasOps.doubleClick()
|
|
})
|
|
|
|
test('Can add filter', async ({ comfyPage }) => {
|
|
await comfyPage.searchBox.addFilter('MODEL', 'Input Type')
|
|
await expectFilterChips(comfyPage, ['MODEL'])
|
|
})
|
|
|
|
test('Outer click dismisses filter panel but keeps search box visible', async ({
|
|
comfyPage
|
|
}) => {
|
|
await comfyPage.searchBox.filterButton.click()
|
|
const panel = comfyPage.searchBox.filterSelectionPanel
|
|
await panel.header.waitFor({ state: 'visible' })
|
|
await comfyPage.page.keyboard.press('Escape')
|
|
|
|
// Verify the filter selection panel is hidden
|
|
await expect(panel.header).not.toBeVisible()
|
|
|
|
// Verify the node search dialog is still visible
|
|
await expect(comfyPage.searchBox.input).toBeVisible()
|
|
})
|
|
|
|
test('Can add multiple filters', async ({ comfyPage }) => {
|
|
await comfyPage.searchBox.addFilter('MODEL', 'Input Type')
|
|
await comfyPage.searchBox.addFilter('CLIP', 'Output Type')
|
|
await expectFilterChips(comfyPage, ['MODEL', 'CLIP'])
|
|
})
|
|
|
|
test('Does not add duplicate filter with same type and value', async ({
|
|
comfyPage
|
|
}) => {
|
|
await comfyPage.searchBox.addFilter('MODEL', 'Input Type')
|
|
await comfyPage.searchBox.addFilter('MODEL', 'Input Type')
|
|
await expectFilterChips(comfyPage, ['MODEL'])
|
|
})
|
|
|
|
test('Can remove filter', async ({ comfyPage }) => {
|
|
await comfyPage.searchBox.addFilter('MODEL', 'Input Type')
|
|
await comfyPage.searchBox.removeFilter(0)
|
|
await expectFilterChips(comfyPage, [])
|
|
})
|
|
|
|
test.describe('Removing from multiple filters', () => {
|
|
test.beforeEach(async ({ comfyPage }) => {
|
|
await comfyPage.searchBox.addFilter('MODEL', 'Input Type')
|
|
await comfyPage.searchBox.addFilter('CLIP', 'Output Type')
|
|
await comfyPage.searchBox.addFilter('utils', 'Category')
|
|
})
|
|
|
|
test('Can remove first filter', async ({ comfyPage }) => {
|
|
await comfyPage.searchBox.removeFilter(0)
|
|
await expectFilterChips(comfyPage, ['CLIP', 'utils'])
|
|
await comfyPage.searchBox.removeFilter(0)
|
|
await expectFilterChips(comfyPage, ['utils'])
|
|
await comfyPage.searchBox.removeFilter(0)
|
|
await expectFilterChips(comfyPage, [])
|
|
})
|
|
|
|
test('Can remove middle filter', async ({ comfyPage }) => {
|
|
await comfyPage.searchBox.removeFilter(1)
|
|
await expectFilterChips(comfyPage, ['MODEL', 'utils'])
|
|
})
|
|
|
|
test('Can remove last filter', async ({ comfyPage }) => {
|
|
await comfyPage.searchBox.removeFilter(2)
|
|
await expectFilterChips(comfyPage, ['MODEL', 'CLIP'])
|
|
})
|
|
})
|
|
})
|
|
|
|
test.describe('Input focus behavior', () => {
|
|
test.beforeEach(async ({ comfyPage }) => {
|
|
await comfyPage.canvasOps.doubleClick()
|
|
})
|
|
|
|
test('focuses input after adding a filter', async ({ comfyPage }) => {
|
|
await comfyPage.searchBox.addFilter('MODEL', 'Input Type')
|
|
await expect(comfyPage.searchBox.input).toHaveFocus()
|
|
})
|
|
|
|
test('focuses input after removing a filter', async ({ comfyPage }) => {
|
|
await comfyPage.searchBox.addFilter('MODEL', 'Input Type')
|
|
await comfyPage.searchBox.removeFilter(0)
|
|
await expect(comfyPage.searchBox.input).toHaveFocus()
|
|
})
|
|
})
|
|
})
|
|
|
|
test.describe('Release context menu', { tag: '@node' }, () => {
|
|
test.beforeEach(async ({ comfyPage }) => {
|
|
await comfyPage.settings.setSetting(
|
|
'Comfy.LinkRelease.Action',
|
|
'context menu'
|
|
)
|
|
await comfyPage.settings.setSetting(
|
|
'Comfy.LinkRelease.ActionShift',
|
|
'search box'
|
|
)
|
|
await comfyPage.settings.setSetting(
|
|
'Comfy.NodeSearchBoxImpl',
|
|
'v1 (legacy)'
|
|
)
|
|
})
|
|
|
|
test(
|
|
'Can trigger on link release',
|
|
{ tag: '@screenshot' },
|
|
async ({ comfyPage }) => {
|
|
await comfyPage.canvasOps.disconnectEdge()
|
|
const contextMenu = comfyPage.page.locator('.litecontextmenu')
|
|
// Wait for context menu with correct title (slot name | slot type)
|
|
// The title shows the output slot name and type from the disconnected link
|
|
await expect(contextMenu.locator('.litemenu-title')).toContainText(
|
|
'CLIP | CLIP'
|
|
)
|
|
await comfyPage.page.mouse.move(10, 10)
|
|
await comfyPage.nextFrame()
|
|
await expect(comfyPage.canvas).toHaveScreenshot(
|
|
'link-release-context-menu.png'
|
|
)
|
|
}
|
|
)
|
|
|
|
test(
|
|
'Can search and add node from context menu',
|
|
{ tag: '@screenshot' },
|
|
async ({ comfyPage, comfyMouse }) => {
|
|
await comfyPage.canvasOps.disconnectEdge()
|
|
await comfyMouse.move({ x: 10, y: 10 })
|
|
await comfyPage.contextMenu.clickMenuItem('Search')
|
|
await comfyPage.nextFrame()
|
|
await comfyPage.searchBox.fillAndSelectFirstNode('CLIP Prompt')
|
|
await expect(comfyPage.canvas).toHaveScreenshot(
|
|
'link-context-menu-search.png'
|
|
)
|
|
}
|
|
)
|
|
|
|
test('Existing user (pre-1.24.1) gets context menu by default on link release', async ({
|
|
comfyPage
|
|
}) => {
|
|
// Start fresh to test existing user behavior
|
|
await comfyPage.setup({ clearStorage: true })
|
|
// Simulate existing user with pre-1.24.1 version
|
|
await comfyPage.settings.setSetting('Comfy.InstalledVersion', '1.23.0')
|
|
await comfyPage.settings.setSetting(
|
|
'Comfy.NodeSearchBoxImpl',
|
|
'v1 (legacy)'
|
|
)
|
|
// Don't set LinkRelease settings explicitly to test versioned defaults
|
|
|
|
await comfyPage.canvasOps.disconnectEdge()
|
|
// Context menu should appear, search box should not
|
|
await expect(comfyPage.searchBox.input).toHaveCount(0)
|
|
const contextMenu = comfyPage.page.locator('.litecontextmenu')
|
|
await expect(contextMenu).toBeVisible()
|
|
})
|
|
|
|
test('Explicit setting overrides versioned defaults', async ({
|
|
comfyPage
|
|
}) => {
|
|
// Start fresh and simulate new user who should get search box by default
|
|
await comfyPage.setup({ clearStorage: true })
|
|
await comfyPage.settings.setSetting('Comfy.InstalledVersion', '1.24.1')
|
|
// But explicitly set to context menu (overriding versioned default)
|
|
await comfyPage.settings.setSetting(
|
|
'Comfy.LinkRelease.Action',
|
|
'context menu'
|
|
)
|
|
await comfyPage.settings.setSetting(
|
|
'Comfy.NodeSearchBoxImpl',
|
|
'v1 (legacy)'
|
|
)
|
|
|
|
await comfyPage.canvasOps.disconnectEdge()
|
|
// Context menu should appear due to explicit setting, not search box
|
|
await expect(comfyPage.searchBox.input).toHaveCount(0)
|
|
const contextMenu = comfyPage.page.locator('.litecontextmenu')
|
|
await expect(contextMenu).toBeVisible()
|
|
})
|
|
})
|