Skip to content

Commit daf7695

Browse files
committed
fix: harden linked widget interactions
1 parent c111815 commit daf7695

5 files changed

Lines changed: 60 additions & 2 deletions

File tree

browser_tests/tests/vueNodes/widgets/linked/linkedStandardWidgets.spec.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ test.describe(
166166
await comfyPage.page.keyboard.type('ignored input')
167167
await expect(focusedLinkedContent).toHaveCount(0)
168168

169+
await comfyPage.nextFrame()
169170
await expect
170171
.poll(() => Promise.all(widgetRefs.map((widget) => widget.getValue())))
171172
.toStrictEqual(valuesBefore)

src/renderer/extensions/vueNodes/components/NodeWidgets.test.ts

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/* eslint-disable testing-library/no-container */
22
/* eslint-disable testing-library/no-node-access */
33
import { createTestingPinia } from '@pinia/testing'
4-
import { render, screen } from '@testing-library/vue'
4+
import { fireEvent, render, screen } from '@testing-library/vue'
55
import { setActivePinia } from 'pinia'
66
import { nextTick } from 'vue'
77
import { describe, expect, it, vi } from 'vitest'
@@ -19,6 +19,11 @@ import { createNodeExecutionId } from '@/types/nodeIdentification'
1919
import { widgetId } from '@/types/widgetId'
2020

2121
const GRAPH_ID = 'graph-test'
22+
const mockShowNodeOptions = vi.hoisted(() => vi.fn())
23+
24+
vi.mock('@/composables/graph/useMoreOptionsMenu', () => ({
25+
showNodeOptions: mockShowNodeOptions
26+
}))
2227

2328
vi.mock('@/renderer/core/canvas/canvasStore', () => ({
2429
useCanvasStore: () => ({
@@ -382,4 +387,39 @@ describe('NodeWidgets', () => {
382387
expect(control).toHaveAttribute('data-linked-display', 'control')
383388
expect(screen.queryByTestId('linked-widget-placeholder')).toBeNull()
384389
})
390+
391+
it('dispatches context menu actions from a mounted linked widget', async () => {
392+
const linkedWidgetId = widgetId(GRAPH_ID, toNodeId('test_node'), 'prompt')
393+
const nodeData = createMockNodeData('TestNode', [
394+
createMockWidget({
395+
widgetId: linkedWidgetId,
396+
name: 'prompt',
397+
nodeId: toNodeId('test_node'),
398+
type: 'text',
399+
slotMetadata: {
400+
index: 0,
401+
linked: true,
402+
type: 'STRING'
403+
}
404+
})
405+
])
406+
407+
const { container } = renderComponent(nodeData, () => {
408+
useWidgetValueStore().registerWidget(linkedWidgetId, {
409+
type: 'text',
410+
value: 'stale local prompt',
411+
options: {}
412+
})
413+
})
414+
415+
const control = container.querySelector('.widget-stub')
416+
expect(control).not.toBeNull()
417+
await fireEvent.contextMenu(control!)
418+
419+
expect(mockShowNodeOptions).toHaveBeenCalledWith(
420+
expect.any(MouseEvent),
421+
'prompt',
422+
toNodeId('test_node')
423+
)
424+
})
385425
})

src/renderer/extensions/vueNodes/widgets/components/WidgetSelectDropdown.test.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@ import WidgetSelectDropdown from '@/renderer/extensions/vueNodes/widgets/compone
1313
import type { SimplifiedWidget } from '@/types/simplifiedWidget'
1414
import { createMockWidget } from './widgetTestUtils'
1515

16+
vi.mock('@/composables/useFeatureFlags', () => ({
17+
useFeatureFlags: () => ({
18+
flags: { modelUploadButtonEnabled: false }
19+
})
20+
}))
21+
1622
const mockCheckState = vi.hoisted(() => vi.fn())
1723
const mockAssetsData = vi.hoisted(() => ({ items: [] as AssetItem[] }))
1824

src/renderer/extensions/vueNodes/widgets/components/WidgetTextarea.test.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,17 @@ describe('WidgetTextarea Value Binding', () => {
174174
expect(textarea.value).toBe('initial content')
175175
})
176176

177+
it('keeps an unlinked disabled textarea readonly without disabling it', () => {
178+
const widget = createTextareaWidget('readable content', {
179+
disabled: true
180+
})
181+
renderComponent(widget, 'readable content')
182+
183+
const textarea = screen.getByRole('textbox')
184+
expect(textarea).toHaveAttribute('readonly')
185+
expect(textarea).toBeEnabled()
186+
})
187+
177188
it('restores the textarea after a link is removed', async () => {
178189
const widget = createTextareaWidget('stale multiline', {
179190
disabled: true

src/renderer/extensions/vueNodes/widgets/components/WidgetTextarea.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
"
3333
:placeholder
3434
:readonly="isReadOnly"
35-
:disabled="Boolean(widget.options?.disabled)"
35+
:disabled="isLinked || undefined"
3636
:aria-hidden="isLinked ? 'true' : undefined"
3737
:inert="isLinked ? true : undefined"
3838
:tabindex="isLinked ? -1 : undefined"

0 commit comments

Comments
 (0)