mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-20 14:30:41 +00:00
## Summary Extract repeated patterns from 12 subgraph Playwright spec files into shared test utilities, reducing duplication by ~142 lines. ## Changes - **What**: New shared helpers for common subgraph test operations: - `SubgraphHelper`: `getSlotCount()`, `getSlotLabel()`, `removeSlot()`, `findSubgraphNodeId()` - `NodeReference`: `delete()` - `subgraphTestUtils`: `serializeAndReload()`, `convertDefaultKSamplerToSubgraph()`, `expectWidgetBelowHeader()`, `collectConsoleWarnings()`, `packAllInteriorNodes()` - Replaced ~72 inline `page.evaluate` blocks and multi-line sequences with single helper calls across 12 spec files ## Review Focus - Behavioral equivalence: every replacement is a mechanical extraction with no test logic changes - API surface of new helpers: naming, parameter types, placement in existing utility classes - Whether any remaining inline patterns in the spec files would benefit from further extraction ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-10629-test-extract-shared-subgraph-E2E-test-utilities-3306d73d365081b0b6b5db52ed0a4552) by [Unito](https://www.unito.io) --------- Co-authored-by: Amp <amp@ampcode.com>
121 lines
3.8 KiB
TypeScript
121 lines
3.8 KiB
TypeScript
import { expect } from '@playwright/test'
|
|
|
|
import { comfyPageFixture as test } from '../fixtures/ComfyPage'
|
|
|
|
test.describe('Subgraph duplicate ID remapping', { tag: ['@subgraph'] }, () => {
|
|
const WORKFLOW = 'subgraphs/subgraph-nested-duplicate-ids'
|
|
|
|
test('All node IDs are globally unique after loading', async ({
|
|
comfyPage
|
|
}) => {
|
|
await comfyPage.workflow.loadWorkflow(WORKFLOW)
|
|
|
|
const result = await comfyPage.page.evaluate(() => {
|
|
const graph = window.app!.canvas.graph!
|
|
// TODO: Extract allGraphs accessor (root + subgraphs) into LGraph
|
|
// TODO: Extract allNodeIds accessor into LGraph
|
|
const allGraphs = [graph, ...graph.subgraphs.values()]
|
|
const allIds = allGraphs
|
|
.flatMap((g) => g._nodes)
|
|
.map((n) => n.id)
|
|
.filter((id): id is number => typeof id === 'number')
|
|
|
|
return { allIds, uniqueCount: new Set(allIds).size }
|
|
})
|
|
|
|
expect(result.uniqueCount).toBe(result.allIds.length)
|
|
expect(result.allIds.length).toBeGreaterThanOrEqual(10)
|
|
})
|
|
|
|
test('Root graph node IDs are preserved as canonical', async ({
|
|
comfyPage
|
|
}) => {
|
|
await comfyPage.workflow.loadWorkflow(WORKFLOW)
|
|
|
|
const rootIds = await comfyPage.page.evaluate(() => {
|
|
const graph = window.app!.canvas.graph!
|
|
return graph._nodes
|
|
.map((n) => n.id)
|
|
.filter((id): id is number => typeof id === 'number')
|
|
.sort((a, b) => a - b)
|
|
})
|
|
|
|
expect(rootIds).toEqual([1, 2, 5])
|
|
})
|
|
|
|
test('Promoted widget tuples are stable after full page reload boot path', async ({
|
|
comfyPage
|
|
}) => {
|
|
await comfyPage.workflow.loadWorkflow(WORKFLOW)
|
|
await comfyPage.nextFrame()
|
|
|
|
const beforeSnapshot =
|
|
await comfyPage.subgraph.getHostPromotedTupleSnapshot()
|
|
expect(beforeSnapshot.length).toBeGreaterThan(0)
|
|
expect(
|
|
beforeSnapshot.some(({ promotedWidgets }) => promotedWidgets.length > 0)
|
|
).toBe(true)
|
|
|
|
await comfyPage.page.reload()
|
|
await comfyPage.page.waitForFunction(() => !!window.app)
|
|
await comfyPage.workflow.loadWorkflow(WORKFLOW)
|
|
await comfyPage.nextFrame()
|
|
|
|
await expect(async () => {
|
|
const afterSnapshot =
|
|
await comfyPage.subgraph.getHostPromotedTupleSnapshot()
|
|
expect(afterSnapshot).toEqual(beforeSnapshot)
|
|
}).toPass({ timeout: 5_000 })
|
|
})
|
|
|
|
test('All links reference valid nodes in their graph', async ({
|
|
comfyPage
|
|
}) => {
|
|
await comfyPage.workflow.loadWorkflow(WORKFLOW)
|
|
|
|
const invalidLinks = await comfyPage.page.evaluate(() => {
|
|
const graph = window.app!.canvas.graph!
|
|
const labeledGraphs: [string, typeof graph][] = [
|
|
['root', graph],
|
|
...[...graph.subgraphs.entries()].map(
|
|
([id, sg]) => [`subgraph:${id}`, sg] as [string, typeof graph]
|
|
)
|
|
]
|
|
|
|
const isNonNegative = (id: number | string) =>
|
|
typeof id === 'number' && id >= 0
|
|
|
|
return labeledGraphs.flatMap(([label, g]) =>
|
|
[...g._links.values()].flatMap((link) =>
|
|
[
|
|
isNonNegative(link.origin_id) &&
|
|
!g._nodes_by_id[link.origin_id] &&
|
|
`${label}: origin_id ${link.origin_id} not found`,
|
|
isNonNegative(link.target_id) &&
|
|
!g._nodes_by_id[link.target_id] &&
|
|
`${label}: target_id ${link.target_id} not found`
|
|
].filter(Boolean)
|
|
)
|
|
)
|
|
})
|
|
|
|
expect(invalidLinks).toEqual([])
|
|
})
|
|
|
|
test('Subgraph navigation works after ID remapping', async ({
|
|
comfyPage
|
|
}) => {
|
|
await comfyPage.workflow.loadWorkflow(WORKFLOW)
|
|
|
|
const subgraphNode = await comfyPage.nodeOps.getNodeRefById('5')
|
|
await subgraphNode.navigateIntoSubgraph()
|
|
|
|
expect(await comfyPage.subgraph.isInSubgraph()).toBe(true)
|
|
|
|
await comfyPage.page.keyboard.press('Escape')
|
|
await comfyPage.nextFrame()
|
|
|
|
expect(await comfyPage.subgraph.isInSubgraph()).toBe(false)
|
|
})
|
|
})
|