mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-20 14:30:41 +00:00
## Summary Alright, alright, alright. These e2e tests have been runnin' around like they're late for somethin', settin' tight little timeouts like the world's gonna end in 250 milliseconds. Man, you gotta *breathe*. Let the framework do its thing. Go slow to go fast, that's what I always say. ## Changes - **What**: Removed ~120 redundant timeout overrides from auto-retrying Playwright assertions (`toBeVisible`, `toBeHidden`, `toHaveCount`, `toBeEnabled`, `toHaveAttribute`, `toContainText`, `expect.poll`) where 5000ms is already the default. Also removed sub-5s timeouts (1s, 2s, 3s) that were just *begging* for flaky failures — like wearin' a belt and suspenders and also holdin' your pants up with both hands. Raised the absurdly short timeouts in `customMatchers.ts` (250ms `toPass` → 5000ms, 256ms poll → default). Kept `timeout: 5000` on `.toPass()` calls (defaults to 0), `.waitFor()`, `waitForRequest`, `waitForFunction`, intentionally-short timeouts inside retry loops, and conditional `.isVisible()/.catch()` checks — those fellas actually need the help. ## Review Focus Every remaining timeout in the diff is there for a *reason*. The ones on `.toPass()` stay because that API defaults to zero — it won't retry at all without one. The ones on `.waitFor()` and `waitForRequest` stay because those are locator actions, not auto-retrying assertions. The intentionally-short ones inside `toPass` retry loops (`interaction.spec.ts`) and the negative assertions (`actionbar.spec.ts` confirming no response arrives) — those are *supposed* to be tight. The short timeouts on regular assertions were actively *encouragin'* flaky failures. That's like settin' your alarm for 4 AM and then gettin' mad you're tired. Just... don't do that, man. Let things take the time they need. 38 files, net -115 lines. Less code, more chill. That's livin'. --------- Co-authored-by: Amp <amp@ampcode.com>
168 lines
5.6 KiB
TypeScript
168 lines
5.6 KiB
TypeScript
import { expect } from '@playwright/test'
|
|
|
|
import type { ComfyPage } from '@e2e/fixtures/ComfyPage'
|
|
import { comfyPageFixture as test } from '@e2e/fixtures/ComfyPage'
|
|
import { SubgraphHelper } from '@e2e/fixtures/helpers/SubgraphHelper'
|
|
import { getPromotedWidgetNames } from '@e2e/helpers/promotedWidgets'
|
|
|
|
const DOM_WIDGET_SELECTOR = '.comfy-multiline-input'
|
|
const VISIBLE_DOM_WIDGET_SELECTOR = `${DOM_WIDGET_SELECTOR}:visible`
|
|
const TEST_WIDGET_CONTENT = 'Test content that should persist'
|
|
|
|
async function openSubgraphById(comfyPage: ComfyPage, nodeId: string) {
|
|
await comfyPage.page.evaluate((targetNodeId) => {
|
|
const node = window.app!.rootGraph.nodes.find(
|
|
(candidate) => String(candidate.id) === targetNodeId
|
|
)
|
|
if (!node || !('subgraph' in node) || !node.subgraph) {
|
|
throw new Error(`Subgraph node ${targetNodeId} not found`)
|
|
}
|
|
|
|
window.app!.canvas.openSubgraph(node.subgraph, node)
|
|
}, nodeId)
|
|
|
|
await expect
|
|
.poll(() =>
|
|
comfyPage.page.evaluate(() => {
|
|
const graph = window.app!.canvas.graph
|
|
return !!graph && 'inputNode' in graph
|
|
})
|
|
)
|
|
.toBe(true)
|
|
}
|
|
|
|
test.describe('Subgraph Promotion DOM', { tag: ['@subgraph'] }, () => {
|
|
test.beforeEach(async ({ comfyPage }) => {
|
|
await comfyPage.settings.setSetting('Comfy.UseNewMenu', 'Disabled')
|
|
await comfyPage.settings.setSetting('Comfy.VueNodes.Enabled', false)
|
|
})
|
|
|
|
test('Promoted seed widget renders in node body, not header', async ({
|
|
comfyPage
|
|
}) => {
|
|
const subgraphNode =
|
|
await comfyPage.subgraph.convertDefaultKSamplerToSubgraph()
|
|
|
|
await comfyPage.settings.setSetting('Comfy.VueNodes.Enabled', true)
|
|
|
|
const subgraphNodeId = String(subgraphNode.id)
|
|
await expect
|
|
.poll(() => getPromotedWidgetNames(comfyPage, subgraphNodeId))
|
|
.toContain('seed')
|
|
|
|
await comfyPage.vueNodes.waitForNodes()
|
|
|
|
const nodeLocator = comfyPage.vueNodes.getNodeLocator(subgraphNodeId)
|
|
await expect(nodeLocator).toBeVisible()
|
|
|
|
const seedWidget = nodeLocator.getByLabel('seed', { exact: true }).first()
|
|
await expect(seedWidget).toBeVisible()
|
|
|
|
await SubgraphHelper.expectWidgetBelowHeader(nodeLocator, seedWidget)
|
|
})
|
|
|
|
test.describe('DOM Widget Promotion', () => {
|
|
test('DOM widget stays visible and preserves content through subgraph navigation', async ({
|
|
comfyPage
|
|
}) => {
|
|
await comfyPage.workflow.loadWorkflow(
|
|
'subgraphs/subgraph-with-promoted-text-widget'
|
|
)
|
|
await comfyPage.nextFrame()
|
|
|
|
const parentTextarea = comfyPage.page.locator(DOM_WIDGET_SELECTOR)
|
|
await expect(parentTextarea).toBeVisible()
|
|
await expect(parentTextarea).toHaveCount(1)
|
|
await parentTextarea.fill(TEST_WIDGET_CONTENT)
|
|
|
|
const subgraphNode = await comfyPage.nodeOps.getNodeRefById('11')
|
|
await expect
|
|
.poll(() => subgraphNode.exists(), 'Subgraph node 11 should exist')
|
|
.toBe(true)
|
|
|
|
await openSubgraphById(comfyPage, '11')
|
|
|
|
const subgraphTextarea = comfyPage.page.locator(DOM_WIDGET_SELECTOR)
|
|
await expect(subgraphTextarea).toBeVisible()
|
|
await expect(subgraphTextarea).toHaveCount(1)
|
|
|
|
await expect(subgraphTextarea).toHaveValue(TEST_WIDGET_CONTENT)
|
|
|
|
await comfyPage.page.keyboard.press('Escape')
|
|
await comfyPage.nextFrame()
|
|
|
|
const backToParentTextarea = comfyPage.page.locator(DOM_WIDGET_SELECTOR)
|
|
await expect(backToParentTextarea).toBeVisible()
|
|
await expect(backToParentTextarea).toHaveCount(1)
|
|
await expect(backToParentTextarea).toHaveValue(TEST_WIDGET_CONTENT)
|
|
})
|
|
|
|
test('DOM elements are cleaned up when subgraph node is removed', async ({
|
|
comfyPage
|
|
}) => {
|
|
await comfyPage.workflow.loadWorkflow(
|
|
'subgraphs/subgraph-with-promoted-text-widget'
|
|
)
|
|
|
|
await expect(comfyPage.page.locator(DOM_WIDGET_SELECTOR)).toHaveCount(1)
|
|
|
|
const subgraphNode = await comfyPage.nodeOps.getNodeRefById('11')
|
|
await subgraphNode.delete()
|
|
|
|
await expect(comfyPage.page.locator(DOM_WIDGET_SELECTOR)).toHaveCount(0)
|
|
})
|
|
|
|
test('DOM elements are cleaned up when widget is disconnected from I/O', async ({
|
|
comfyPage
|
|
}) => {
|
|
await comfyPage.settings.setSetting('Comfy.UseNewMenu', 'Top')
|
|
|
|
await comfyPage.workflow.loadWorkflow(
|
|
'subgraphs/subgraph-with-promoted-text-widget'
|
|
)
|
|
|
|
await expect(comfyPage.page.locator(DOM_WIDGET_SELECTOR)).toHaveCount(1)
|
|
|
|
const subgraphNode = await comfyPage.nodeOps.getNodeRefById('11')
|
|
await expect
|
|
.poll(() => subgraphNode.exists(), 'Subgraph node 11 should exist')
|
|
.toBe(true)
|
|
|
|
await openSubgraphById(comfyPage, '11')
|
|
|
|
await comfyPage.subgraph.removeSlot('input', 'text')
|
|
|
|
await comfyPage.subgraph.exitViaBreadcrumb()
|
|
|
|
await expect(
|
|
comfyPage.page.locator(VISIBLE_DOM_WIDGET_SELECTOR)
|
|
).toHaveCount(0)
|
|
})
|
|
|
|
test('Multiple promoted widgets are handled correctly', async ({
|
|
comfyPage
|
|
}) => {
|
|
await comfyPage.workflow.loadWorkflow(
|
|
'subgraphs/subgraph-with-multiple-promoted-widgets'
|
|
)
|
|
|
|
const visibleWidgets = comfyPage.page.locator(VISIBLE_DOM_WIDGET_SELECTOR)
|
|
await expect(visibleWidgets).toHaveCount(2)
|
|
const parentCount = await visibleWidgets.count()
|
|
|
|
const subgraphNode = await comfyPage.nodeOps.getNodeRefById('11')
|
|
await expect
|
|
.poll(() => subgraphNode.exists(), 'Subgraph node 11 should exist')
|
|
.toBe(true)
|
|
|
|
await openSubgraphById(comfyPage, '11')
|
|
|
|
await expect(visibleWidgets).toHaveCount(parentCount)
|
|
|
|
await comfyPage.subgraph.exitViaBreadcrumb()
|
|
|
|
await expect(visibleWidgets).toHaveCount(parentCount)
|
|
})
|
|
})
|
|
})
|