Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
306ae55
Remove type text from slot
benceruleanlu Jul 21, 2025
fe1b61d
Format slots LR
benceruleanlu Jul 21, 2025
b7144ea
Deduplicate slots/widgets
benceruleanlu Jul 21, 2025
a5e2b45
Remove node body padding
benceruleanlu Jul 24, 2025
3748cf4
Add slots
benceruleanlu Jul 24, 2025
d0f70bc
Make node body gap consistent
benceruleanlu Jul 24, 2025
b0c35bc
Add slots UI
benceruleanlu Jul 24, 2025
7190b78
Strict height
benceruleanlu Jul 24, 2025
e72d29c
Add larger hitbox
benceruleanlu Jul 24, 2025
ac8af8b
nit
benceruleanlu Jul 24, 2025
d02d7aa
Update constant name
benceruleanlu Jul 24, 2025
b35a621
Add feature flag sync
benceruleanlu Jul 24, 2025
d8e5a5c
Temp tsc silencer
benceruleanlu Jul 24, 2025
a29f65c
Add event forwarding
benceruleanlu Jul 24, 2025
4d8a1cf
Hide "No Widgets" text
benceruleanlu Jul 25, 2025
a94a1fc
pr-1.5 to widgets
benceruleanlu Jul 25, 2025
4ee0a39
Fix slot visibility on widgets
benceruleanlu Jul 25, 2025
f959de7
nit
benceruleanlu Jul 25, 2025
8541800
Rename to vueNodesMode
benceruleanlu Jul 25, 2025
a099d9b
Disable DOM widgets in vue nodes mode
benceruleanlu Jul 25, 2025
9ebc97a
Merge to feature flag
benceruleanlu Jul 25, 2025
f2260d3
Add node header boilerplate test
benceruleanlu Jul 25, 2025
bf89129
feat: Add escape key support to EditableText component
benceruleanlu Jul 25, 2025
94f4479
Add test fixtures
benceruleanlu Jul 26, 2025
e44c6ae
Add data-testid to vue node header
benceruleanlu Jul 26, 2025
38292bb
Add vue node header tests
benceruleanlu Jul 26, 2025
692c77d
Fix handles node collapsing test
benceruleanlu Jul 26, 2025
b3d8182
Fix MARKDOWN => TEXTAREA test
benceruleanlu Jul 28, 2025
cd84802
Comment out misleading test expectation
benceruleanlu Jul 28, 2025
432bc4e
Squashed 'src/lib/litegraph/' content from commit f0f8e9e66
benceruleanlu Jul 28, 2025
14571ba
Merge commit '432bc4eea4ac3daaff0fda20b8ce875324b90f41' as 'src/lib/l…
benceruleanlu Jul 28, 2025
b732ed7
Add TypeScript path mapping for local litegraph subtree
benceruleanlu Jul 28, 2025
12c10cc
Remove @comfyorg/litegraph npm dependency
benceruleanlu Jul 28, 2025
86e7a14
Exclude litegraph from ESLint
benceruleanlu Jul 28, 2025
38acfbe
Remove litegraph-specific config files
benceruleanlu Jul 28, 2025
dc80e48
Configure litegraph as TypeScript composite project
benceruleanlu Jul 29, 2025
ba8f186
Revert "Add TypeScript path mapping for local litegraph subtree"
benceruleanlu Jul 29, 2025
44d80e0
Fix TypeScript errors in Rectangle.ts
benceruleanlu Jul 29, 2025
d6025b5
Fix imports
benceruleanlu Jul 29, 2025
11174de
Remove unused eslint
benceruleanlu Jul 29, 2025
cb5e8de
Remove duplicated/unused packages
benceruleanlu Jul 29, 2025
1e307e8
Fix pre-commit hook to use vue-tsc --build
benceruleanlu Jul 29, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 131 additions & 0 deletions browser_tests/fixtures/utils/vueNodeFixtures.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import type { Locator, Page } from '@playwright/test'

import type { NodeReference } from './litegraphUtils'

/**
* VueNodeFixture provides Vue-specific testing utilities for interacting with
* Vue node components. It bridges the gap between litegraph node references
* and Vue UI components.
*/
export class VueNodeFixture {
constructor(
private readonly nodeRef: NodeReference,
private readonly page: Page
) {}

/**
* Get the node's header element using data-testid
*/
async getHeader(): Promise<Locator> {
const nodeId = this.nodeRef.id
return this.page.locator(`[data-testid="node-header-${nodeId}"]`)
}

/**
* Get the node's title element
*/
async getTitleElement(): Promise<Locator> {
const header = await this.getHeader()
return header.locator('[data-testid="node-title"]')
}

/**
* Get the current title text
*/
async getTitle(): Promise<string> {
const titleElement = await this.getTitleElement()
return (await titleElement.textContent()) || ''
}

/**
* Set a new title by double-clicking and entering text
*/
async setTitle(newTitle: string): Promise<void> {
const titleElement = await this.getTitleElement()
await titleElement.dblclick()

const input = (await this.getHeader()).locator(
'[data-testid="node-title-input"]'
)
await input.fill(newTitle)
await input.press('Enter')
}

/**
* Cancel title editing
*/
async cancelTitleEdit(): Promise<void> {
const titleElement = await this.getTitleElement()
await titleElement.dblclick()

const input = (await this.getHeader()).locator(
'[data-testid="node-title-input"]'
)
await input.press('Escape')
}

/**
* Check if the title is currently being edited
*/
async isEditingTitle(): Promise<boolean> {
const header = await this.getHeader()
const input = header.locator('[data-testid="node-title-input"]')
return await input.isVisible()
}

/**
* Get the collapse/expand button
*/
async getCollapseButton(): Promise<Locator> {
const header = await this.getHeader()
return header.locator('[data-testid="node-collapse-button"]')
}

/**
* Toggle the node's collapsed state
*/
async toggleCollapse(): Promise<void> {
const button = await this.getCollapseButton()
await button.click()
}

/**
* Get the collapse icon element
*/
async getCollapseIcon(): Promise<Locator> {
const button = await this.getCollapseButton()
return button.locator('i')
}

/**
* Get the collapse icon's CSS classes
*/
async getCollapseIconClass(): Promise<string> {
const icon = await this.getCollapseIcon()
return (await icon.getAttribute('class')) || ''
}

/**
* Check if the collapse button is visible
*/
async isCollapseButtonVisible(): Promise<boolean> {
const button = await this.getCollapseButton()
return await button.isVisible()
}

/**
* Get the node's body/content element
*/
async getBody(): Promise<Locator> {
const nodeId = this.nodeRef.id
return this.page.locator(`[data-testid="node-body-${nodeId}"]`)
}

/**
* Check if the node body is visible (not collapsed)
*/
async isBodyVisible(): Promise<boolean> {
const body = await this.getBody()
return await body.isVisible()
}
}
138 changes: 138 additions & 0 deletions browser_tests/tests/vueNodes/NodeHeader.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import {
comfyExpect as expect,
comfyPageFixture as test
} from '../../fixtures/ComfyPage'
import { VueNodeFixture } from '../../fixtures/utils/vueNodeFixtures'

test.describe('NodeHeader', () => {
test.beforeEach(async ({ comfyPage }) => {
await comfyPage.setSetting('Comfy.UseNewMenu', 'Enabled')
await comfyPage.setSetting('Comfy.Graph.CanvasMenu', false)
await comfyPage.setSetting('Comfy.EnableTooltips', true)
await comfyPage.setup()
// Load single SaveImage node workflow (positioned below menu bar)
await comfyPage.loadWorkflow('single_save_image_node')
})

test('displays node title', async ({ comfyPage }) => {
// Get the single SaveImage node from the workflow
const nodes = await comfyPage.getNodeRefsByType('SaveImage')
expect(nodes.length).toBeGreaterThanOrEqual(1)

const node = nodes[0]
const vueNode = new VueNodeFixture(node, comfyPage.page)

const title = await vueNode.getTitle()
expect(title).toBe('Save Image')

// Verify title is visible in the header
const header = await vueNode.getHeader()
await expect(header).toContainText('Save Image')
})

test('allows title renaming', async ({ comfyPage }) => {
// Get the single SaveImage node from the workflow
const nodes = await comfyPage.getNodeRefsByType('SaveImage')
const node = nodes[0]
const vueNode = new VueNodeFixture(node, comfyPage.page)

// Test renaming with Enter
await vueNode.setTitle('My Custom Sampler')
const newTitle = await vueNode.getTitle()
expect(newTitle).toBe('My Custom Sampler')

// Verify the title is displayed
const header = await vueNode.getHeader()
await expect(header).toContainText('My Custom Sampler')

// Test cancel with Escape
const titleElement = await vueNode.getTitleElement()
await titleElement.dblclick()
await comfyPage.nextFrame()

// Type a different value but cancel
const input = (await vueNode.getHeader()).locator(
'[data-testid="node-title-input"]'
)
await input.fill('This Should Be Cancelled')
await input.press('Escape')
await comfyPage.nextFrame()

// Title should remain as the previously saved value
const titleAfterCancel = await vueNode.getTitle()
expect(titleAfterCancel).toBe('My Custom Sampler')
})

test('handles node collapsing', async ({ comfyPage }) => {
// Get the single SaveImage node from the workflow
const nodes = await comfyPage.getNodeRefsByType('SaveImage')
const node = nodes[0]
const vueNode = new VueNodeFixture(node, comfyPage.page)

// Initially should not be collapsed
expect(await node.isCollapsed()).toBe(false)
const body = await vueNode.getBody()
await expect(body).toBeVisible()

// Collapse the node
await vueNode.toggleCollapse()
expect(await node.isCollapsed()).toBe(true)

// Verify node content is hidden
const collapsedSize = await node.getSize()
await expect(body).not.toBeVisible()

// Expand again
await vueNode.toggleCollapse()
expect(await node.isCollapsed()).toBe(false)
await expect(body).toBeVisible()

// Size should be restored
const expandedSize = await node.getSize()
expect(expandedSize.height).toBeGreaterThanOrEqual(collapsedSize.height)
})

test('shows collapse/expand icon state', async ({ comfyPage }) => {
// Get the single SaveImage node from the workflow
const nodes = await comfyPage.getNodeRefsByType('SaveImage')
const node = nodes[0]
const vueNode = new VueNodeFixture(node, comfyPage.page)

// Check initial expanded state icon
let iconClass = await vueNode.getCollapseIconClass()
expect(iconClass).toContain('pi-chevron-down')

// Collapse and check icon
await vueNode.toggleCollapse()
iconClass = await vueNode.getCollapseIconClass()
expect(iconClass).toContain('pi-chevron-right')

// Expand and check icon
await vueNode.toggleCollapse()
iconClass = await vueNode.getCollapseIconClass()
expect(iconClass).toContain('pi-chevron-down')
})

test('preserves title when collapsing/expanding', async ({ comfyPage }) => {
// Get the single SaveImage node from the workflow
const nodes = await comfyPage.getNodeRefsByType('SaveImage')
const node = nodes[0]
const vueNode = new VueNodeFixture(node, comfyPage.page)

// Set custom title
await vueNode.setTitle('Test Sampler')
expect(await vueNode.getTitle()).toBe('Test Sampler')

// Collapse
await vueNode.toggleCollapse()
expect(await vueNode.getTitle()).toBe('Test Sampler')

// Expand
await vueNode.toggleCollapse()
expect(await vueNode.getTitle()).toBe('Test Sampler')

// Verify title is still displayed
const header = await vueNode.getHeader()
await expect(header).toContainText('Test Sampler')
})
})
3 changes: 2 additions & 1 deletion eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ export default [
ignores: [
'src/scripts/*',
'src/extensions/core/*',
'src/types/vue-shim.d.ts'
'src/types/vue-shim.d.ts',
'src/lib/litegraph/**/*'
]
},
{
Expand Down
2 changes: 1 addition & 1 deletion lint-staged.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ export default {

'./**/*.{ts,tsx,vue,mts}': (stagedFiles) => [
...formatAndEslint(stagedFiles),
'vue-tsc --noEmit'
'npm run typecheck'
]
}

Expand Down
7 changes: 0 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"build": "npm run typecheck && vite build",
"build:types": "vite build --config vite.types.config.mts && node scripts/prepare-types.js",
"zipdist": "node scripts/zipdist.js",
"typecheck": "vue-tsc --noEmit",
"typecheck": "vue-tsc --build",
"format": "prettier --write './**/*.{js,ts,tsx,vue,mts}'",
"format:check": "prettier --check './**/*.{js,ts,tsx,vue,mts}'",
"test:browser": "npx playwright test",
Expand Down Expand Up @@ -76,7 +76,6 @@
"@alloc/quick-lru": "^5.2.0",
"@atlaskit/pragmatic-drag-and-drop": "^1.3.1",
"@comfyorg/comfyui-electron-types": "^0.4.43",
"@comfyorg/litegraph": "^0.16.3",
"@primevue/forms": "^4.2.5",
"@primevue/themes": "^4.2.5",
"@sentry/vue": "^8.48.0",
Expand Down
Loading