fix: browser_tests Phase 1 - mechanical fixes

- Rename dragAndDrop to dragDrop (7 occurrences)

- Add override modifiers in SidebarTab.ts (4 fixes)

- Remove .ts import extensions in actionbar.spec.ts

- Prefix unused variables with underscore (9 files)

- Fix ESLint import() type annotation in globals.d.ts

Reduces typecheck:browser errors from 229 to 215

Amp-Thread-ID: https://ampcode.com/threads/T-019c1787-c781-761d-b95a-4844e909e64c
Co-authored-by: Amp <amp@ampcode.com>
This commit is contained in:
Alexander Brown
2026-01-31 21:05:08 -08:00
parent 6cd105fdf0
commit b56045c462
34 changed files with 181 additions and 156 deletions

View File

@@ -96,7 +96,7 @@ class ComfyMenu {
async getThemeId() {
return await this.page.evaluate(async () => {
return await window.app.ui.settings.getSettingValue('Comfy.ColorPalette')
return await window.app!.ui.settings.getSettingValue('Comfy.ColorPalette')
})
}
}
@@ -382,7 +382,7 @@ export class ComfyPage {
async setFocusMode(focusMode: boolean) {
await this.page.evaluate((focusMode) => {
window.app.extensionManager.focusMode = focusMode
window.app!.extensionManager.focusMode = focusMode
}, focusMode)
await this.nextFrame()
}

View File

@@ -33,7 +33,7 @@ class SidebarTab {
}
export class NodeLibrarySidebarTab extends SidebarTab {
constructor(public readonly page: Page) {
constructor(public override readonly page: Page) {
super(page, 'node-library')
}
@@ -59,12 +59,12 @@ export class NodeLibrarySidebarTab extends SidebarTab {
return this.tabContainer.locator('.new-folder-button')
}
async open() {
override async open() {
await super.open()
await this.nodeLibraryTree.waitFor({ state: 'visible' })
}
async close() {
override async close() {
if (!this.tabButton.isVisible()) {
return
}
@@ -101,7 +101,7 @@ export class NodeLibrarySidebarTab extends SidebarTab {
}
export class WorkflowsSidebarTab extends SidebarTab {
constructor(public readonly page: Page) {
constructor(public override readonly page: Page) {
super(page, 'workflows')
}

View File

@@ -85,7 +85,7 @@ export class Topbar {
// Wait for workflow service to finish saving
await this.page.waitForFunction(
() => !window.app.extensionManager.workflow.isBusy,
() => !window.app!.extensionManager.workflow.isBusy,
undefined,
{ timeout: 3000 }
)

View File

@@ -93,13 +93,13 @@ export class CanvasHelper {
async getScale(): Promise<number> {
return this.page.evaluate(() => {
return window.app.canvas.ds.scale
return window.app!.canvas.ds.scale
})
}
async setScale(scale: number): Promise<void> {
await this.page.evaluate((s) => {
window.app.canvas.ds.scale = s
window.app!.canvas.ds.scale = s
}, scale)
await this.nextFrame()
}
@@ -108,13 +108,13 @@ export class CanvasHelper {
pos: [number, number]
): Promise<[number, number]> {
return this.page.evaluate((pos) => {
return window.app.canvas.ds.convertOffsetToCanvas(pos)
return window.app!.canvas.ds.convertOffsetToCanvas(pos)
}, pos)
}
async getNodeCenterByTitle(title: string): Promise<Position | null> {
return this.page.evaluate((title) => {
const app = window.app
const app = window.app!
const node = app.graph.nodes.find(
(n: { title: string }) => n.title === title
)
@@ -129,7 +129,7 @@ export class CanvasHelper {
async getGroupPosition(title: string): Promise<Position> {
const pos = await this.page.evaluate((title) => {
const groups = window.app.graph.groups
const groups = window.app!.graph.groups
const group = groups.find((g: { title: string }) => g.title === title)
if (!group) return null
return { x: group.pos[0], y: group.pos[1] }
@@ -145,7 +145,7 @@ export class CanvasHelper {
}): Promise<void> {
const { name, deltaX, deltaY } = options
const screenPos = await this.page.evaluate((title) => {
const app = window.app
const app = window.app!
const groups = app.graph.groups
const group = groups.find((g: { title: string }) => g.title === title)
if (!group) return null

View File

@@ -5,7 +5,7 @@ import type { KeyboardHelper } from './KeyboardHelper'
export class ClipboardHelper {
constructor(
private readonly keyboard: KeyboardHelper,
private readonly canvas: Locator
private readonly _canvas: Locator
) {}
async copy(locator?: Locator | null): Promise<void> {

View File

@@ -7,7 +7,7 @@ export class CommandHelper {
async executeCommand(commandId: string): Promise<void> {
await this.page.evaluate((id: string) => {
return window.app.extensionManager.command.execute(id)
return window.app!.extensionManager.command.execute(id)
}, commandId)
}
@@ -17,7 +17,7 @@ export class CommandHelper {
): Promise<void> {
await this.page.evaluate(
({ commandId, commandStr }) => {
const app = window.app
const app = window.app!
const randomSuffix = Math.random().toString(36).substring(2, 8)
const extensionName = `TestExtension_${randomSuffix}`
@@ -41,7 +41,7 @@ export class CommandHelper {
): Promise<void> {
await this.page.evaluate(
({ keyCombo, commandStr }) => {
const app = window.app
const app = window.app!
const randomSuffix = Math.random().toString(36).substring(2, 8)
const extensionName = `TestExtension_${randomSuffix}`
const commandId = `TestCommand_${randomSuffix}`

View File

@@ -35,7 +35,7 @@ export class NodeOperationsHelper {
async getNodes(): Promise<LGraphNode[]> {
return await this.page.evaluate(() => {
return window.app.graph.nodes
return window.app!.graph.nodes
})
}
@@ -47,7 +47,7 @@ export class NodeOperationsHelper {
async getFirstNodeRef(): Promise<NodeReference | null> {
const id = await this.page.evaluate(() => {
return window.app.graph.nodes[0]?.id
return window.app!.graph.nodes[0]?.id
})
if (!id) return null
return this.getNodeRefById(id)
@@ -66,7 +66,7 @@ export class NodeOperationsHelper {
await this.page.evaluate(
({ type, includeSubgraph }) => {
const graph = (
includeSubgraph ? window.app.canvas.graph : window.app.graph
includeSubgraph ? window.app!.canvas.graph : window.app!.graph
) as LGraph
const nodes = graph.nodes
return nodes
@@ -83,8 +83,8 @@ export class NodeOperationsHelper {
return Promise.all(
(
await this.page.evaluate((title) => {
return window.app.graph.nodes
.filter((n: LGraphNode) => n.title === title)
return window
.app!.graph.nodes.filter((n: LGraphNode) => n.title === title)
.map((n: LGraphNode) => n.id)
}, title)
).map((id: NodeId) => this.getNodeRefById(id))

View File

@@ -6,7 +6,7 @@ export class SettingsHelper {
async setSetting(settingId: string, settingValue: unknown): Promise<void> {
await this.page.evaluate(
async ({ id, value }) => {
await window.app.extensionManager.setting.set(id, value)
await window.app!.extensionManager.setting.set(id, value)
},
{ id: settingId, value: settingValue }
)
@@ -14,7 +14,7 @@ export class SettingsHelper {
async getSetting<T = unknown>(settingId: string): Promise<T> {
return await this.page.evaluate(async (id) => {
return await window.app.extensionManager.setting.get(id)
return await window.app!.extensionManager.setting.get(id)
}, settingId)
}
}

View File

@@ -27,7 +27,7 @@ export class SubgraphHelper {
const foundSlot = await this.page.evaluate(
async (params) => {
const { slotType, action, targetSlotName } = params
const app = window.app
const app = window.app!
const currentGraph = app.canvas.graph
// Check if we're in a subgraph
@@ -242,7 +242,7 @@ export class SubgraphHelper {
? await targetSlot.getPosition() // Connect to existing slot
: await targetSlot.getOpenSlotPosition() // Create new slot
await this.comfyPage.dragAndDrop(
await this.comfyPage.dragDrop(
await sourceSlot.getPosition(),
targetPosition
)
@@ -267,7 +267,7 @@ export class SubgraphHelper {
const targetPosition = await targetSlot.getPosition()
await this.comfyPage.dragAndDrop(sourcePosition, targetPosition)
await this.comfyPage.dragDrop(sourcePosition, targetPosition)
await this.comfyPage.nextFrame()
}
@@ -287,7 +287,7 @@ export class SubgraphHelper {
? await targetSlot.getPosition() // Connect to existing slot
: await targetSlot.getOpenSlotPosition() // Create new slot
await this.comfyPage.dragAndDrop(
await this.comfyPage.dragDrop(
await sourceSlot.getPosition(),
targetPosition
)
@@ -310,7 +310,7 @@ export class SubgraphHelper {
? await sourceSlot.getPosition() // Connect from existing slot
: await sourceSlot.getOpenSlotPosition() // Create new slot
await this.comfyPage.dragAndDrop(
await this.comfyPage.dragDrop(
sourcePosition,
await targetSlot.getPosition()
)

View File

@@ -45,7 +45,7 @@ export class WorkflowHelper {
}
await this.comfyPage.page.evaluate(async () => {
await window.app.extensionManager.workflow.syncWorkflows()
await window.app!.extensionManager.workflow.syncWorkflows()
})
// Wait for Vue to re-render the workflow list
@@ -86,7 +86,7 @@ export class WorkflowHelper {
async getUndoQueueSize(): Promise<number | undefined> {
return this.comfyPage.page.evaluate(() => {
const workflow = (window.app.extensionManager as WorkspaceStore).workflow
const workflow = (window.app!.extensionManager as WorkspaceStore).workflow
.activeWorkflow
return workflow?.changeTracker.undoQueue.length
})
@@ -94,7 +94,7 @@ export class WorkflowHelper {
async getRedoQueueSize(): Promise<number | undefined> {
return this.comfyPage.page.evaluate(() => {
const workflow = (window.app.extensionManager as WorkspaceStore).workflow
const workflow = (window.app!.extensionManager as WorkspaceStore).workflow
.activeWorkflow
return workflow?.changeTracker.redoQueue.length
})
@@ -102,7 +102,7 @@ export class WorkflowHelper {
async isCurrentWorkflowModified(): Promise<boolean | undefined> {
return this.comfyPage.page.evaluate(() => {
return (window.app.extensionManager as WorkspaceStore).workflow
return (window.app!.extensionManager as WorkspaceStore).workflow
.activeWorkflow?.isModified
})
}
@@ -110,7 +110,7 @@ export class WorkflowHelper {
async getExportedWorkflow(options?: { api?: boolean }): Promise<any> {
const api = options?.api ?? false
return this.comfyPage.page.evaluate(async (api) => {
return (await window.app.graphToPrompt())[api ? 'output' : 'workflow']
return (await window.app!.graphToPrompt())[api ? 'output' : 'workflow']
}, api)
}
}

View File

@@ -23,7 +23,7 @@ export class SubgraphSlotReference {
async getPosition(): Promise<Position> {
const pos: [number, number] = await this.comfyPage.page.evaluate(
([type, slotName]) => {
const currentGraph = window.app.canvas.graph
const currentGraph = window.app!.canvas.graph
// Check if we're in a subgraph
if (currentGraph.constructor.name !== 'Subgraph') {
@@ -52,7 +52,7 @@ export class SubgraphSlotReference {
}
// Convert from offset to canvas coordinates
const canvasPos = window.app.canvas.ds.convertOffsetToCanvas([
const canvasPos = window.app!.canvas.ds.convertOffsetToCanvas([
slot.pos[0],
slot.pos[1]
])
@@ -70,7 +70,7 @@ export class SubgraphSlotReference {
async getOpenSlotPosition(): Promise<Position> {
const pos: [number, number] = await this.comfyPage.page.evaluate(
([type]) => {
const currentGraph = window.app.canvas.graph
const currentGraph = window.app!.canvas.graph
if (currentGraph.constructor.name !== 'Subgraph') {
throw new Error(
@@ -86,7 +86,7 @@ export class SubgraphSlotReference {
}
// Convert from offset to canvas coordinates
const canvasPos = window.app.canvas.ds.convertOffsetToCanvas([
const canvasPos = window.app!.canvas.ds.convertOffsetToCanvas([
node.emptySlot.pos[0],
node.emptySlot.pos[1]
])
@@ -112,11 +112,11 @@ class NodeSlotReference {
const pos: [number, number] = await this.node.comfyPage.page.evaluate(
([type, id, index]) => {
// Use canvas.graph to get the current graph (works in both main graph and subgraphs)
const node = window.app.canvas.graph.getNodeById(id)
const node = window.app!.canvas.graph.getNodeById(id)
if (!node) throw new Error(`Node ${id} not found.`)
const rawPos = node.getConnectionPos(type === 'input', index)
const convertedPos = window.app.canvas.ds.convertOffsetToCanvas(rawPos)
const convertedPos = window.app!.canvas.ds.convertOffsetToCanvas(rawPos)
// Debug logging - convert Float64Arrays to regular arrays for visibility
console.warn(
@@ -126,7 +126,7 @@ class NodeSlotReference {
nodeSize: [node.size[0], node.size[1]],
rawConnectionPos: [rawPos[0], rawPos[1]],
convertedPos: [convertedPos[0], convertedPos[1]],
currentGraphType: window.app.canvas.graph.constructor.name
currentGraphType: window.app!.canvas.graph.constructor.name
}
)
@@ -142,7 +142,7 @@ class NodeSlotReference {
async getLinkCount() {
return await this.node.comfyPage.page.evaluate(
([type, id, index]) => {
const node = window.app.canvas.graph.getNodeById(id)
const node = window.app!.canvas.graph.getNodeById(id)
if (!node) throw new Error(`Node ${id} not found.`)
if (type === 'input') {
return node.inputs[index].link == null ? 0 : 1
@@ -155,7 +155,7 @@ class NodeSlotReference {
async removeLinks() {
await this.node.comfyPage.page.evaluate(
([type, id, index]) => {
const node = window.app.canvas.graph.getNodeById(id)
const node = window.app!.canvas.graph.getNodeById(id)
if (!node) throw new Error(`Node ${id} not found.`)
if (type === 'input') {
node.disconnectInput(index)
@@ -180,15 +180,15 @@ class NodeWidgetReference {
async getPosition(): Promise<Position> {
const pos: [number, number] = await this.node.comfyPage.page.evaluate(
([id, index]) => {
const node = window.app.canvas.graph.getNodeById(id)
const node = window.app!.canvas.graph.getNodeById(id)
if (!node) throw new Error(`Node ${id} not found.`)
const widget = node.widgets[index]
if (!widget) throw new Error(`Widget ${index} not found.`)
const [x, y, w, h] = node.getBounding()
return window.app.canvasPosToClientPos([
const [x, y, w, _h] = node.getBounding()
return window.app!.canvasPosToClientPos([
x + w / 2,
y + window.LiteGraph['NODE_TITLE_HEIGHT'] + widget.last_y + 1
y + window.LiteGraph!['NODE_TITLE_HEIGHT'] + widget.last_y + 1
])
},
[this.node.id, this.index] as const
@@ -205,7 +205,7 @@ class NodeWidgetReference {
async getSocketPosition(): Promise<Position> {
const pos: [number, number] = await this.node.comfyPage.page.evaluate(
([id, index]) => {
const node = window.app.graph.getNodeById(id)
const node = window.app!.graph.getNodeById(id)
if (!node) throw new Error(`Node ${id} not found.`)
const widget = node.widgets[index]
if (!widget) throw new Error(`Widget ${index} not found.`)
@@ -216,9 +216,9 @@ class NodeWidgetReference {
if (!slot) throw new Error(`Socket ${widget.name} not found.`)
const [x, y] = node.getBounding()
return window.app.canvasPosToClientPos([
return window.app!.canvasPosToClientPos([
x + slot.pos[0],
y + slot.pos[1] + window.LiteGraph['NODE_TITLE_HEIGHT']
y + slot.pos[1] + window.LiteGraph!['NODE_TITLE_HEIGHT']
])
},
[this.node.id, this.index] as const
@@ -239,7 +239,7 @@ class NodeWidgetReference {
const pos = await this.getPosition()
const canvas = this.node.comfyPage.canvas
const canvasPos = (await canvas.boundingBox())!
await this.node.comfyPage.dragAndDrop(
await this.node.comfyPage.dragDrop(
{
x: canvasPos.x + pos.x,
y: canvasPos.y + pos.y
@@ -254,7 +254,7 @@ class NodeWidgetReference {
async getValue() {
return await this.node.comfyPage.page.evaluate(
([id, index]) => {
const node = window.app.graph.getNodeById(id)
const node = window.app!.graph.getNodeById(id)
if (!node) throw new Error(`Node ${id} not found.`)
const widget = node.widgets[index]
if (!widget) throw new Error(`Widget ${index} not found.`)
@@ -271,7 +271,7 @@ export class NodeReference {
) {}
async exists(): Promise<boolean> {
return await this.comfyPage.page.evaluate((id) => {
const node = window.app.canvas.graph.getNodeById(id)
const node = window.app!.canvas.graph.getNodeById(id)
return !!node
}, this.id)
}
@@ -290,7 +290,7 @@ export class NodeReference {
async getBounding(): Promise<Position & Size> {
const [x, y, width, height]: [number, number, number, number] =
await this.comfyPage.page.evaluate((id) => {
const node = window.app.canvas.graph.getNodeById(id)
const node = window.app!.canvas.graph.getNodeById(id)
if (!node) throw new Error('Node not found')
return node.getBounding()
}, this.id)
@@ -328,7 +328,7 @@ export class NodeReference {
async getProperty<T>(prop: string): Promise<T> {
return await this.comfyPage.page.evaluate(
([id, prop]) => {
const node = window.app.canvas.graph.getNodeById(id)
const node = window.app!.canvas.graph.getNodeById(id)
if (!node) throw new Error('Node not found')
return node[prop]
},
@@ -389,7 +389,7 @@ export class NodeReference {
) {
const originSlot = await this.getOutput(originSlotIndex)
const targetWidget = await targetNode.getWidget(targetWidgetIndex)
await this.comfyPage.dragAndDrop(
await this.comfyPage.dragDrop(
await originSlot.getPosition(),
await targetWidget.getSocketPosition()
)
@@ -402,7 +402,7 @@ export class NodeReference {
) {
const originSlot = await this.getOutput(originSlotIndex)
const targetSlot = await targetNode.getInput(targetSlotIndex)
await this.comfyPage.dragAndDrop(
await this.comfyPage.dragDrop(
await originSlot.getPosition(),
await targetSlot.getPosition()
)
@@ -452,7 +452,7 @@ export class NodeReference {
}
async navigateIntoSubgraph() {
const titleHeight = await this.comfyPage.page.evaluate(() => {
return window.LiteGraph['NODE_TITLE_HEIGHT']
return window.LiteGraph!['NODE_TITLE_HEIGHT']
})
const nodePos = await this.getPosition()
const nodeSize = await this.getSize()
@@ -466,7 +466,7 @@ export class NodeReference {
const checkIsInSubgraph = async () => {
return this.comfyPage.page.evaluate(() => {
const graph = window.app.canvas.graph
const graph = window.app!.canvas.graph
return graph?.constructor?.name === 'Subgraph'
})
}

View File

@@ -5,7 +5,7 @@ import { backupPath } from './utils/backupUtils'
dotenv.config()
export default function globalSetup(config: FullConfig) {
export default function globalSetup(_config: FullConfig) {
if (!process.env.CI) {
if (process.env.TEST_COMFYUI_DIR) {
backupPath([process.env.TEST_COMFYUI_DIR, 'user'])

View File

@@ -5,7 +5,7 @@ import { restorePath } from './utils/backupUtils'
dotenv.config()
export default function globalTeardown(config: FullConfig) {
export default function globalTeardown(_config: FullConfig) {
if (!process.env.CI && process.env.TEST_COMFYUI_DIR) {
restorePath([process.env.TEST_COMFYUI_DIR, 'user'])
restorePath([process.env.TEST_COMFYUI_DIR, 'models'])

View File

@@ -1,9 +1,9 @@
import type { Response } from '@playwright/test'
import { expect, mergeTests } from '@playwright/test'
import type { StatusWsMessage } from '../../src/schemas/apiSchema.ts'
import { comfyPageFixture } from '../fixtures/ComfyPage.ts'
import { webSocketFixture } from '../fixtures/ws.ts'
import type { StatusWsMessage } from '../../src/schemas/apiSchema'
import { comfyPageFixture } from '../fixtures/ComfyPage'
import { webSocketFixture } from '../fixtures/ws'
const test = mergeTests(comfyPageFixture, webSocketFixture)
@@ -49,7 +49,7 @@ test.describe('Actionbar', { tag: '@ui' }, () => {
// Find and set the width on the latent node
const triggerChange = async (value: number) => {
return await comfyPage.page.evaluate((value) => {
const node = window.app.graph._nodes.find(
const node = window.app!.graph!._nodes.find(
(n) => n.type === 'EmptyLatentImage'
)
node.widgets[0].value = value

View File

@@ -11,7 +11,7 @@ test.describe('Browser tab title', { tag: '@smoke' }, () => {
test('Can display workflow name', async ({ comfyPage }) => {
const workflowName = await comfyPage.page.evaluate(async () => {
return window.app.extensionManager.workflow.activeWorkflow.filename
return window.app!.extensionManager.workflow.activeWorkflow.filename
})
expect(await comfyPage.page.title()).toBe(`*${workflowName} - ComfyUI`)
})
@@ -22,7 +22,7 @@ test.describe('Browser tab title', { tag: '@smoke' }, () => {
comfyPage
}) => {
const workflowName = await comfyPage.page.evaluate(async () => {
return window.app.extensionManager.workflow.activeWorkflow.filename
return window.app!.extensionManager.workflow.activeWorkflow.filename
})
expect(await comfyPage.page.title()).toBe(`${workflowName} - ComfyUI`)
@@ -38,7 +38,7 @@ test.describe('Browser tab title', { tag: '@smoke' }, () => {
// Delete the saved workflow for cleanup.
await comfyPage.page.evaluate(async () => {
return window.app.extensionManager.workflow.activeWorkflow.delete()
return window.app!.extensionManager.workflow.activeWorkflow.delete()
})
})
})

View File

@@ -7,12 +7,12 @@ import { DefaultGraphPositions } from '../fixtures/constants/defaultGraphPositio
async function beforeChange(comfyPage: ComfyPage) {
await comfyPage.page.evaluate(() => {
window.app.canvas.emitBeforeChange()
window.app!.canvas!.emitBeforeChange()
})
}
async function afterChange(comfyPage: ComfyPage) {
await comfyPage.page.evaluate(() => {
window.app.canvas.emitAfterChange()
window.app!.canvas!.emitAfterChange()
})
}
@@ -159,7 +159,7 @@ test.describe('Change Tracker', { tag: '@workflow' }, () => {
test('Can detect changes in workflow.extra', async ({ comfyPage }) => {
expect(await comfyPage.workflow.getUndoQueueSize()).toBe(0)
await comfyPage.page.evaluate(() => {
window.app.graph.extra.foo = 'bar'
window.app!.graph!.extra.foo = 'bar'
})
// Click empty space to trigger a change detection.
await comfyPage.canvasOps.clickEmptySpace(

View File

@@ -179,7 +179,7 @@ test.describe('Color Palette', { tag: ['@screenshot', '@settings'] }, () => {
test('Can add custom color palette', async ({ comfyPage }) => {
await comfyPage.page.evaluate((p) => {
window.app.extensionManager.colorPalette.addCustomColorPalette(p)
window.app!.extensionManager.colorPalette.addCustomColorPalette(p)
}, customColorPalettes.obsidian_dark)
expect(await comfyPage.toast.getToastErrorCount()).toBe(0)

View File

@@ -41,7 +41,7 @@ test.describe('Keybindings', { tag: '@keyboard' }, () => {
test('Should handle async command errors', async ({ comfyPage }) => {
await comfyPage.command.registerCommand('TestCommand', async () => {
await new Promise<void>((resolve, reject) =>
await new Promise<void>((_resolve, reject) =>
setTimeout(() => {
reject(new Error('Test error'))
}, 5)

View File

@@ -345,7 +345,7 @@ test.describe('Error dialog', () => {
comfyPage
}) => {
await comfyPage.page.evaluate(() => {
const graph = window.graph
const graph = window.graph!
graph.configure = () => {
throw new Error('Error on configure!')
}
@@ -361,7 +361,7 @@ test.describe('Error dialog', () => {
comfyPage
}) => {
await comfyPage.page.evaluate(async () => {
const app = window.app
const app = window.app!
app.api.queuePrompt = () => {
throw new Error('Error on queuePrompt!')
}
@@ -391,7 +391,7 @@ test.describe('Signin dialog', () => {
await textBox.press('Control+c')
await comfyPage.page.evaluate(() => {
void window.app.extensionManager.dialog.showSignInDialog()
void window.app!.extensionManager.dialog.showSignInDialog()
})
const input = comfyPage.page.locator('#comfy-org-sign-in-password')

View File

@@ -10,7 +10,7 @@ test.describe('Topbar commands', () => {
test('Should allow registering topbar commands', async ({ comfyPage }) => {
await comfyPage.page.evaluate(() => {
window.app.registerExtension({
window.app!.registerExtension({
name: 'TestExtension1',
commands: [
{
@@ -39,7 +39,7 @@ test.describe('Topbar commands', () => {
}) => {
await comfyPage.command.registerCommand('foo', () => alert(1))
await comfyPage.page.evaluate(() => {
window.app.registerExtension({
window.app!.registerExtension({
name: 'TestExtension1',
menuCommands: [
{
@@ -56,7 +56,7 @@ test.describe('Topbar commands', () => {
test('Should allow registering keybindings', async ({ comfyPage }) => {
await comfyPage.page.evaluate(() => {
const app = window.app
const app = window.app!
app.registerExtension({
name: 'TestExtension1',
commands: [
@@ -83,7 +83,7 @@ test.describe('Topbar commands', () => {
test.describe('Settings', () => {
test('Should allow adding settings', async ({ comfyPage }) => {
await comfyPage.page.evaluate(() => {
window.app.registerExtension({
window.app!.registerExtension({
name: 'TestExtension1',
settings: [
{
@@ -113,7 +113,7 @@ test.describe('Topbar commands', () => {
test('Should allow setting boolean settings', async ({ comfyPage }) => {
await comfyPage.page.evaluate(() => {
window.app.registerExtension({
window.app!.registerExtension({
name: 'TestExtension1',
settings: [
{
@@ -197,7 +197,7 @@ test.describe('Topbar commands', () => {
comfyPage
}) => {
await comfyPage.page.evaluate((config) => {
window.app.registerExtension({
window.app!.registerExtension({
name: 'TestExtension1',
settings: [
{
@@ -230,7 +230,7 @@ test.describe('Topbar commands', () => {
test.describe('About panel', () => {
test('Should allow adding badges', async ({ comfyPage }) => {
await comfyPage.page.evaluate(() => {
window.app.registerExtension({
window.app!.registerExtension({
name: 'TestExtension1',
aboutPageBadges: [
{
@@ -253,8 +253,8 @@ test.describe('Topbar commands', () => {
test.describe('Dialog', () => {
test('Should allow showing a prompt dialog', async ({ comfyPage }) => {
await comfyPage.page.evaluate(() => {
void window.app.extensionManager.dialog
.prompt({
void window
.app!.extensionManager.dialog.prompt({
title: 'Test Prompt',
message: 'Test Prompt Message'
})
@@ -273,8 +273,8 @@ test.describe('Topbar commands', () => {
comfyPage
}) => {
await comfyPage.page.evaluate(() => {
void window.app.extensionManager.dialog
.confirm({
void window
.app!.extensionManager.dialog.confirm({
title: 'Test Confirm',
message: 'Test Confirm Message'
})
@@ -290,8 +290,8 @@ test.describe('Topbar commands', () => {
test('Should allow dismissing a dialog', async ({ comfyPage }) => {
await comfyPage.page.evaluate(() => {
window['value'] = 'foo'
void window.app.extensionManager.dialog
.confirm({
void window
.app!.extensionManager.dialog.confirm({
title: 'Test Confirm',
message: 'Test Confirm Message'
})
@@ -315,7 +315,7 @@ test.describe('Topbar commands', () => {
}) => {
// Register an extension with a selection toolbox command
await comfyPage.page.evaluate(() => {
window.app.registerExtension({
window.app!.registerExtension({
name: 'TestExtension1',
commands: [
{

View File

@@ -13,7 +13,7 @@ test.describe('Graph', { tag: ['@smoke', '@canvas'] }, () => {
await comfyPage.workflow.loadWorkflow('inputs/input_order_swap')
expect(
await comfyPage.page.evaluate(() => {
return window.app.graph.links.get(1)?.target_slot
return window.app!.graph!.links.get(1)?.target_slot
})
).toBe(1)
})

View File

@@ -26,7 +26,7 @@ test.describe('Graph Canvas Menu', { tag: ['@screenshot', '@canvas'] }, () => {
'canvas-with-hidden-links.png'
)
const hiddenLinkRenderMode = await comfyPage.page.evaluate(() => {
return window.LiteGraph.HIDDEN_LINK
return window.LiteGraph!.HIDDEN_LINK
})
expect(await comfyPage.settings.getSetting('Comfy.LinkRenderMode')).toBe(
hiddenLinkRenderMode

View File

@@ -23,7 +23,9 @@ test.describe('Group Node', { tag: '@node' }, () => {
await libraryTab.open()
})
test('Is added to node library sidebar', async ({ comfyPage }) => {
test('Is added to node library sidebar', async ({
comfyPage: _comfyPage
}) => {
expect(await libraryTab.getFolder('group nodes').count()).toBe(1)
})
@@ -158,7 +160,7 @@ test.describe('Group Node', { tag: '@node' }, () => {
const totalInputCount = await comfyPage.page.evaluate((nodeName) => {
const {
extra: { groupNodes }
} = window.app.graph
} = window.app!.graph!
const { nodes } = groupNodes[nodeName]
return nodes.reduce((acc: number, node) => {
return acc + node.inputs.length
@@ -166,7 +168,7 @@ test.describe('Group Node', { tag: '@node' }, () => {
}, groupNodeName)
const visibleInputCount = await comfyPage.page.evaluate((id) => {
const node = window.app.graph.getNodeById(id)
const node = window.app!.graph!.getNodeById(id)
return node.inputs.length
}, groupNodeId)
@@ -233,7 +235,7 @@ test.describe('Group Node', { tag: '@node' }, () => {
const isRegisteredLitegraph = async (comfyPage: ComfyPage) => {
return await comfyPage.page.evaluate((nodeType: string) => {
return !!window.LiteGraph.registered_node_types[nodeType]
return !!window.LiteGraph!.registered_node_types[nodeType]
}, GROUP_NODE_TYPE)
}
@@ -307,12 +309,12 @@ test.describe('Group Node', { tag: '@node' }, () => {
await comfyPage.menu.topbar.triggerTopbarCommand(['New'])
await comfyPage.clipboard.paste()
const currentGraphState = await comfyPage.page.evaluate(() =>
window.app.graph.serialize()
window.app!.graph!.serialize()
)
await test.step('Load workflow containing a group node pasted from a different workflow', async () => {
await comfyPage.page.evaluate(
(workflow) => window.app.loadGraphData(workflow),
(workflow) => window.app!.loadGraphData(workflow),
currentGraphState
)
await comfyPage.nextFrame()

View File

@@ -15,7 +15,7 @@ test.describe('LOD Threshold', { tag: ['@screenshot', '@canvas'] }, () => {
// Get initial LOD state and settings
const initialState = await comfyPage.page.evaluate(() => {
const canvas = window.app.canvas
const canvas = window.app!.canvas
return {
lowQuality: canvas.low_quality,
scale: canvas.ds.scale,
@@ -36,7 +36,7 @@ test.describe('LOD Threshold', { tag: ['@screenshot', '@canvas'] }, () => {
await comfyPage.nextFrame()
const aboveThresholdState = await comfyPage.page.evaluate(() => {
const canvas = window.app.canvas
const canvas = window.app!.canvas
return {
lowQuality: canvas.low_quality,
scale: canvas.ds.scale
@@ -54,7 +54,7 @@ test.describe('LOD Threshold', { tag: ['@screenshot', '@canvas'] }, () => {
// Check that LOD is now active
const zoomedOutState = await comfyPage.page.evaluate(() => {
const canvas = window.app.canvas
const canvas = window.app!.canvas
return {
lowQuality: canvas.low_quality,
scale: canvas.ds.scale
@@ -70,7 +70,7 @@ test.describe('LOD Threshold', { tag: ['@screenshot', '@canvas'] }, () => {
// Check that LOD is now inactive
const zoomedInState = await comfyPage.page.evaluate(() => {
const canvas = window.app.canvas
const canvas = window.app!.canvas
return {
lowQuality: canvas.low_quality,
scale: canvas.ds.scale
@@ -94,7 +94,7 @@ test.describe('LOD Threshold', { tag: ['@screenshot', '@canvas'] }, () => {
// Check that font size updated
const newState = await comfyPage.page.evaluate(() => {
const canvas = window.app.canvas
const canvas = window.app!.canvas
return {
minFontSize: canvas.min_font_size_for_lod
}
@@ -105,7 +105,7 @@ test.describe('LOD Threshold', { tag: ['@screenshot', '@canvas'] }, () => {
// At default zoom, LOD should still be inactive (scale is exactly 1.0, not less than)
const lodState = await comfyPage.page.evaluate(() => {
return window.app.canvas.low_quality
return window.app!.canvas.low_quality
})
expect(lodState).toBe(false)
@@ -114,7 +114,7 @@ test.describe('LOD Threshold', { tag: ['@screenshot', '@canvas'] }, () => {
await comfyPage.nextFrame()
const afterZoom = await comfyPage.page.evaluate(() => {
const canvas = window.app.canvas
const canvas = window.app!.canvas
return {
lowQuality: canvas.low_quality,
scale: canvas.ds.scale
@@ -139,7 +139,7 @@ test.describe('LOD Threshold', { tag: ['@screenshot', '@canvas'] }, () => {
// LOD should remain disabled even at very low zoom
const state = await comfyPage.page.evaluate(() => {
const canvas = window.app.canvas
const canvas = window.app!.canvas
return {
lowQuality: canvas.low_quality,
scale: canvas.ds.scale,
@@ -164,8 +164,8 @@ test.describe('LOD Threshold', { tag: ['@screenshot', '@canvas'] }, () => {
// Zoom to target level
await comfyPage.page.evaluate((zoom) => {
window.app.canvas.ds.scale = zoom
window.app.canvas.setDirty(true, true)
window.app!.canvas.ds.scale = zoom
window.app!.canvas.setDirty(true, true)
}, targetZoom)
await comfyPage.nextFrame()
@@ -175,7 +175,7 @@ test.describe('LOD Threshold', { tag: ['@screenshot', '@canvas'] }, () => {
)
const lowQualityState = await comfyPage.page.evaluate(() => {
const canvas = window.app.canvas
const canvas = window.app!.canvas
return {
lowQuality: canvas.low_quality,
scale: canvas.ds.scale
@@ -196,7 +196,7 @@ test.describe('LOD Threshold', { tag: ['@screenshot', '@canvas'] }, () => {
)
const highQualityState = await comfyPage.page.evaluate(() => {
const canvas = window.app.canvas
const canvas = window.app!.canvas
return {
lowQuality: canvas.low_quality,
scale: canvas.ds.scale

View File

@@ -11,7 +11,7 @@ test.describe('Menu', { tag: '@ui' }, () => {
const initialChildrenCount = await comfyPage.menu.buttons.count()
await comfyPage.page.evaluate(async () => {
window.app.extensionManager.registerSidebarTab({
window.app!.extensionManager.registerSidebarTab({
id: 'search',
icon: 'pi pi-search',
title: 'search',
@@ -155,7 +155,7 @@ test.describe('Menu', { tag: '@ui' }, () => {
test('Can catch error when executing command', async ({ comfyPage }) => {
await comfyPage.page.evaluate(() => {
window.app.registerExtension({
window.app!.registerExtension({
name: 'TestExtension1',
commands: [
{

View File

@@ -26,21 +26,21 @@ test.describe('Remote COMBO Widget', { tag: '@widget' }, () => {
nodeName: string
): Promise<string[] | undefined> => {
return await comfyPage.page.evaluate((name) => {
const node = window.app.graph.nodes.find((node) => node.title === name)
const node = window.app!.graph!.nodes.find((node) => node.title === name)
return node.widgets[0].options.values
}, nodeName)
}
const getWidgetValue = async (comfyPage: ComfyPage, nodeName: string) => {
return await comfyPage.page.evaluate((name) => {
const node = window.app.graph.nodes.find((node) => node.title === name)
const node = window.app!.graph!.nodes.find((node) => node.title === name)
return node.widgets[0].value
}, nodeName)
}
const clickRefreshButton = (comfyPage: ComfyPage, nodeName: string) => {
return comfyPage.page.evaluate((name) => {
const node = window.app.graph.nodes.find((node) => node.title === name)
const node = window.app!.graph!.nodes.find((node) => node.title === name)
const buttonWidget = node.widgets.find((w) => w.name === 'refresh')
return buttonWidget?.callback()
}, nodeName)
@@ -92,7 +92,7 @@ test.describe('Remote COMBO Widget', { tag: '@widget' }, () => {
await comfyPage.workflow.loadWorkflow('inputs/remote_widget')
const node = await comfyPage.page.evaluate((name) => {
return window.app.graph.nodes.find((node) => node.title === name)
return window.app!.graph!.nodes.find((node) => node.title === name)
}, nodeName)
expect(node).toBeDefined()
@@ -196,7 +196,7 @@ test.describe('Remote COMBO Widget', { tag: '@widget' }, () => {
// Fulfill each request with a unique timestamp
await comfyPage.page.route(
'**/api/models/checkpoints**',
async (route, request) => {
async (route, _request) => {
await route.fulfill({
body: JSON.stringify([Date.now()]),
status: 200

View File

@@ -3,7 +3,6 @@ import { expect } from '@playwright/test'
import { comfyPageFixture as test } from '../fixtures/ComfyPage'
// Constants
const INITIAL_NAME = 'initial_slot_name'
const RENAMED_NAME = 'renamed_slot_name'
const SECOND_RENAMED_NAME = 'second_renamed_name'
@@ -27,7 +26,7 @@ test.describe('Subgraph Slot Rename Dialog', { tag: '@subgraph' }, () => {
// Get initial slot label
const initialInputLabel = await comfyPage.page.evaluate(() => {
const graph = window.app.canvas.graph
const graph = window.app!.canvas.graph
return graph.inputs?.[0]?.label || graph.inputs?.[0]?.name || null
})
@@ -56,7 +55,7 @@ test.describe('Subgraph Slot Rename Dialog', { tag: '@subgraph' }, () => {
// Verify the rename worked
const afterFirstRename = await comfyPage.page.evaluate(() => {
const graph = window.app.canvas.graph
const graph = window.app!.canvas.graph
const slot = graph.inputs?.[0]
return {
label: slot?.label || null,
@@ -99,7 +98,7 @@ test.describe('Subgraph Slot Rename Dialog', { tag: '@subgraph' }, () => {
// Verify the second rename worked
const afterSecondRename = await comfyPage.page.evaluate(() => {
const graph = window.app.canvas.graph
const graph = window.app!.canvas.graph
return graph.inputs?.[0]?.label || null
})
expect(afterSecondRename).toBe(SECOND_RENAMED_NAME)
@@ -115,7 +114,7 @@ test.describe('Subgraph Slot Rename Dialog', { tag: '@subgraph' }, () => {
// Get initial output slot label
const initialOutputLabel = await comfyPage.page.evaluate(() => {
const graph = window.app.canvas.graph
const graph = window.app!.canvas.graph
return graph.outputs?.[0]?.label || graph.outputs?.[0]?.name || null
})

View File

@@ -27,7 +27,7 @@ test.describe('Subgraph Operations', { tag: ['@slow', '@subgraph'] }, () => {
type: 'inputs' | 'outputs'
): Promise<number> {
return await comfyPage.page.evaluate((slotType) => {
return window.app.canvas.graph[slotType]?.length || 0
return window.app!.canvas.graph[slotType]?.length || 0
}, type)
}
@@ -36,7 +36,7 @@ test.describe('Subgraph Operations', { tag: ['@slow', '@subgraph'] }, () => {
comfyPage: typeof test.prototype.comfyPage
): Promise<number> {
return await comfyPage.page.evaluate(() => {
return window.app.canvas.graph.nodes?.length || 0
return window.app!.canvas.graph.nodes?.length || 0
})
}
@@ -45,7 +45,7 @@ test.describe('Subgraph Operations', { tag: ['@slow', '@subgraph'] }, () => {
comfyPage: typeof test.prototype.comfyPage
): Promise<boolean> {
return await comfyPage.page.evaluate(() => {
const graph = window.app.canvas.graph
const graph = window.app!.canvas.graph
return graph?.constructor?.name === 'Subgraph'
})
}
@@ -132,7 +132,7 @@ test.describe('Subgraph Operations', { tag: ['@slow', '@subgraph'] }, () => {
await subgraphNode.navigateIntoSubgraph()
const initialInputLabel = await comfyPage.page.evaluate(() => {
const graph = window.app.canvas.graph
const graph = window.app!.canvas.graph
return graph.inputs?.[0]?.label || null
})
@@ -151,7 +151,7 @@ test.describe('Subgraph Operations', { tag: ['@slow', '@subgraph'] }, () => {
await comfyPage.nextFrame()
const newInputName = await comfyPage.page.evaluate(() => {
const graph = window.app.canvas.graph
const graph = window.app!.canvas.graph
return graph.inputs?.[0]?.label || null
})
@@ -166,7 +166,7 @@ test.describe('Subgraph Operations', { tag: ['@slow', '@subgraph'] }, () => {
await subgraphNode.navigateIntoSubgraph()
const initialInputLabel = await comfyPage.page.evaluate(() => {
const graph = window.app.canvas.graph
const graph = window.app!.canvas.graph
return graph.inputs?.[0]?.label || null
})
@@ -183,7 +183,7 @@ test.describe('Subgraph Operations', { tag: ['@slow', '@subgraph'] }, () => {
await comfyPage.nextFrame()
const newInputName = await comfyPage.page.evaluate(() => {
const graph = window.app.canvas.graph
const graph = window.app!.canvas.graph
return graph.inputs?.[0]?.label || null
})
@@ -198,7 +198,7 @@ test.describe('Subgraph Operations', { tag: ['@slow', '@subgraph'] }, () => {
await subgraphNode.navigateIntoSubgraph()
const initialOutputLabel = await comfyPage.page.evaluate(() => {
const graph = window.app.canvas.graph
const graph = window.app!.canvas.graph
return graph.outputs?.[0]?.label || null
})
@@ -216,7 +216,7 @@ test.describe('Subgraph Operations', { tag: ['@slow', '@subgraph'] }, () => {
await comfyPage.nextFrame()
const newOutputName = await comfyPage.page.evaluate(() => {
const graph = window.app.canvas.graph
const graph = window.app!.canvas.graph
return graph.outputs?.[0]?.label || null
})
@@ -233,7 +233,7 @@ test.describe('Subgraph Operations', { tag: ['@slow', '@subgraph'] }, () => {
await subgraphNode.navigateIntoSubgraph()
const initialInputLabel = await comfyPage.page.evaluate(() => {
const graph = window.app.canvas.graph
const graph = window.app!.canvas.graph
return graph.inputs?.[0]?.label || null
})
@@ -254,7 +254,7 @@ test.describe('Subgraph Operations', { tag: ['@slow', '@subgraph'] }, () => {
await comfyPage.nextFrame()
const newInputName = await comfyPage.page.evaluate(() => {
const graph = window.app.canvas.graph
const graph = window.app!.canvas.graph
return graph.inputs?.[0]?.label || null
})
@@ -271,7 +271,7 @@ test.describe('Subgraph Operations', { tag: ['@slow', '@subgraph'] }, () => {
await subgraphNode.navigateIntoSubgraph()
const initialInputLabel = await comfyPage.page.evaluate(() => {
const graph = window.app.canvas.graph
const graph = window.app!.canvas.graph
return graph.inputs?.[0]?.label || null
})
@@ -326,7 +326,7 @@ test.describe('Subgraph Operations', { tag: ['@slow', '@subgraph'] }, () => {
await comfyPage.nextFrame()
const newInputName = await comfyPage.page.evaluate(() => {
const graph = window.app.canvas.graph
const graph = window.app!.canvas.graph
return graph.inputs?.[0]?.label || null
})
@@ -350,7 +350,7 @@ test.describe('Subgraph Operations', { tag: ['@slow', '@subgraph'] }, () => {
test('Can create subgraph from selected nodes', async ({ comfyPage }) => {
await comfyPage.workflow.loadWorkflow('default')
const initialNodeCount = await getGraphNodeCount(comfyPage)
const _initialNodeCount = await getGraphNodeCount(comfyPage)
await comfyPage.keyboard.selectAll()
await comfyPage.nextFrame()
@@ -459,7 +459,7 @@ test.describe('Subgraph Operations', { tag: ['@slow', '@subgraph'] }, () => {
const initialNodeCount = await getGraphNodeCount(comfyPage)
const nodesInSubgraph = await comfyPage.page.evaluate(() => {
const nodes = window.app.canvas.graph.nodes
const nodes = window.app!.canvas.graph.nodes
return nodes?.[0]?.id || null
})
@@ -689,7 +689,7 @@ test.describe('Subgraph Operations', { tag: ['@slow', '@subgraph'] }, () => {
// Check that the subgraph node has no widgets after removing the text slot
const widgetCount = await comfyPage.page.evaluate(() => {
return window.app.canvas.graph.nodes[0].widgets?.length || 0
return window.app!.canvas.graph.nodes[0].widgets?.length || 0
})
expect(widgetCount).toBe(0)

View File

@@ -10,7 +10,7 @@ test.describe('Settings Search functionality', { tag: '@settings' }, () => {
test.beforeEach(async ({ comfyPage }) => {
// Register test settings to verify hidden/deprecated filtering
await comfyPage.page.evaluate(() => {
window.app.registerExtension({
window.app!.registerExtension({
name: 'TestSettingsExtension',
settings: [
{

View File

@@ -13,17 +13,17 @@ test.describe('Vue Widget Reactivity', () => {
'css=[data-testid="node-body-4"] > .lg-node-widgets > div'
)
await comfyPage.page.evaluate(() => {
const node = window.graph._nodes_by_id['4']
const node = window.graph!._nodes_by_id['4']
node.widgets.push(node.widgets[0])
})
await expect(loadCheckpointNode).toHaveCount(2)
await comfyPage.page.evaluate(() => {
const node = window.graph._nodes_by_id['4']
const node = window.graph!._nodes_by_id['4']
node.widgets[2] = node.widgets[0]
})
await expect(loadCheckpointNode).toHaveCount(3)
await comfyPage.page.evaluate(() => {
const node = window.graph._nodes_by_id['4']
const node = window.graph!._nodes_by_id['4']
node.widgets.splice(0, 0, node.widgets[0])
})
await expect(loadCheckpointNode).toHaveCount(4)
@@ -33,17 +33,17 @@ test.describe('Vue Widget Reactivity', () => {
'css=[data-testid="node-body-3"] > .lg-node-widgets > div'
)
await comfyPage.page.evaluate(() => {
const node = window.graph._nodes_by_id['3']
const node = window.graph!._nodes_by_id['3']
node.widgets.pop()
})
await expect(loadCheckpointNode).toHaveCount(5)
await comfyPage.page.evaluate(() => {
const node = window.graph._nodes_by_id['3']
const node = window.graph!._nodes_by_id['3']
node.widgets.length--
})
await expect(loadCheckpointNode).toHaveCount(4)
await comfyPage.page.evaluate(() => {
const node = window.graph._nodes_by_id['3']
const node = window.graph!._nodes_by_id['3']
node.widgets.splice(0, 1)
})
await expect(loadCheckpointNode).toHaveCount(3)

View File

@@ -58,8 +58,10 @@ test.describe('Combo text widget', { tag: ['@screenshot', '@widget'] }, () => {
}) => {
const getComboValues = async () =>
comfyPage.page.evaluate(() => {
return window.app.graph.nodes
.find((node) => node.title === 'Node With Optional Combo Input')
return window
.app!.graph!.nodes.find(
(node) => node.title === 'Node With Optional Combo Input'
)
.widgets.find((widget) => widget.name === 'optional_combo_input')
.options.values
})
@@ -93,8 +95,10 @@ test.describe('Combo text widget', { tag: ['@screenshot', '@widget'] }, () => {
await comfyPage.nextFrame()
// get the combo widget's values
const comboValues = await comfyPage.page.evaluate(() => {
return window.app.graph.nodes
.find((node) => node.title === 'Node With V2 Combo Input')
return window
.app!.graph!.nodes.find(
(node) => node.title === 'Node With V2 Combo Input'
)
.widgets.find((widget) => widget.name === 'combo_input').options.values
})
expect(comboValues).toEqual(['A', 'B'])
@@ -121,7 +125,7 @@ test.describe('Slider widget', { tag: ['@screenshot', '@widget'] }, () => {
const widget = await node.getWidget(0)
await comfyPage.page.evaluate(() => {
const widget = window.app.graph.nodes[0].widgets[0]
const widget = window.app!.graph!.nodes[0].widgets[0]
widget.callback = (value: number) => {
window.widgetValue = value
}
@@ -142,7 +146,7 @@ test.describe('Number widget', { tag: ['@screenshot', '@widget'] }, () => {
const node = (await comfyPage.nodeOps.getFirstNodeRef())!
const widget = await node.getWidget(0)
await comfyPage.page.evaluate(() => {
const widget = window.app.graph.nodes[0].widgets[0]
const widget = window.app!.graph!.nodes[0].widgets[0]
widget.callback = (value: number) => {
window.widgetValue = value
}
@@ -166,8 +170,8 @@ test.describe(
await comfyPage.workflow.loadWorkflow('nodes/single_ksampler')
await comfyPage.page.evaluate(() => {
window.graph.nodes[0].addWidget('number', 'new_widget', 10)
window.graph.setDirtyCanvas(true, true)
window.graph!.nodes[0].addWidget('number', 'new_widget', 10)
window.graph!.setDirtyCanvas(true, true)
})
await expect(comfyPage.canvas).toHaveScreenshot(
@@ -349,7 +353,7 @@ test.describe(
await comfyPage.page.evaluate(
([loadId, saveId]) => {
// Set the output of the SaveAnimatedWEBP node to equal the loader node's image
window.app.nodeOutputs[saveId] = window.app.nodeOutputs[loadId]
window.app!.nodeOutputs[saveId] = window.app!.nodeOutputs[loadId]
app.canvas.setDirty(true)
},
[loadAnimatedWebpNode.id, saveAnimatedWebpNode.id]

View File

@@ -5,7 +5,8 @@
"noEmit": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"resolveJsonModule": true
"resolveJsonModule": true,
"types": ["vite/client"]
},
"include": [
"**/*.ts",

View File

@@ -1,7 +1,6 @@
import type { ComfyApp } from '@/scripts/app'
import type { LGraph } from '@/lib/litegraph'
import type { LiteGraphGlobal } from '@/lib/litegraph/src/LiteGraphGlobal'
import type { LGraphBadge } from '@/lib/litegraph/src/LGraphBadge'
interface AppReadiness {
featureFlagsReceived: boolean
@@ -31,6 +30,11 @@ declare global {
__capturedMessages?: CapturedMessages
__appReadiness?: AppReadiness
}
const app: ComfyApp | undefined
const graph: LGraph | undefined
const LiteGraph: LiteGraphGlobal | undefined
const LGraphBadge: typeof LGraphBadge | undefined
}
export {}

View File

@@ -14,6 +14,21 @@ See `docs/testing/*.md` for detailed patterns.
- Prefer specific selectors (role, label, test-id)
- Test across viewports
## Window Globals
Browser tests access `window.app`, `window.graph`, and `window.LiteGraph` which are
optional in the main app types. In E2E tests, use non-null assertions (`!`):
```typescript
window.app!.graph!.nodes
window.LiteGraph!.registered_node_types
```
This is the **only context** where non-null assertions are acceptable.
**TODO:** Consolidate these references into a central utility (e.g., `getApp()`) that
performs proper runtime type checking, removing the need for scattered `!` assertions.
## Test Tags
Tags are respected by config: