Files
ComfyUI_frontend/tests-ui/tests/renderer/extensions/vueNodes/composables/useNodeZIndex.test.ts
Christian Byrne 29dbfa3f60 fix: Vue Node <-> Litegraph node height offset normalization (#6966)
## Summary

Changes the layout store to treat node sizes as body-only measurements
while LiteGraph continues to reason about full heights. DOM-driven
updates are tagged with `LayoutSource.DOM`, which lets the store strip
the title height exactly once before persisting. That classification (a
new mutation source - `LayoutSource.DOM`) is accurate because those
mutations are triggered by the browser’s layout engine via
ResizeObserver, rather than by direct calls into the layout APIs (e.g.,
`moveNodeTo`, `useNodeDrag`). So all sources are:

- `LayoutSource.DOM`: browser layout/ResizeObserver measurements that
include the title bar
- `LayoutSource.Vue`: direct Vue-driven mutations routed through the
layout store
- `LayoutSource.Canvas`: legacy LiteGraph/canvas updates that will be
phased out over time
- `LayoutSource.External`: for multiplayer or syncing with a when going
online after making changes offline (in teams/workspace)

When layout state flows back into LiteGraph we add the title height just
in time for `liteNode.setSize`, so LiteGraph’s rendering stays
unchanged. This makes Vue node resizing and workflow persistence
deterministic - multiline widgets hold their dimensions across reloads
because every path that crosses the layout/LiteGraph boundary performs
the same normalization.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6966-normalize-height-at-sites-2b76d73d365081b6bcb4f4ce6a27663a)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-11-26 17:37:24 -07:00

101 lines
3.2 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from 'vitest'
import { useLayoutMutations } from '@/renderer/core/layout/operations/layoutMutations'
import { LayoutSource } from '@/renderer/core/layout/types'
import { useNodeZIndex } from '@/renderer/extensions/vueNodes/composables/useNodeZIndex'
// Mock the layout mutations module
vi.mock('@/renderer/core/layout/operations/layoutMutations', () => ({
useLayoutMutations: vi.fn()
}))
const mockedUseLayoutMutations = vi.mocked(useLayoutMutations)
describe('useNodeZIndex', () => {
beforeEach(() => {
vi.clearAllMocks()
})
describe('bringNodeToFront', () => {
it('should bring node to front with default source', () => {
const mockSetSource = vi.fn()
const mockBringNodeToFront = vi.fn()
mockedUseLayoutMutations.mockReturnValue({
setSource: mockSetSource,
bringNodeToFront: mockBringNodeToFront
} as Partial<ReturnType<typeof useLayoutMutations>> as ReturnType<
typeof useLayoutMutations
>)
const { bringNodeToFront } = useNodeZIndex()
bringNodeToFront('node1')
expect(mockSetSource).toHaveBeenCalledWith(LayoutSource.Vue)
expect(mockBringNodeToFront).toHaveBeenCalledWith('node1')
})
it('should bring node to front with custom source', () => {
const mockSetSource = vi.fn()
const mockBringNodeToFront = vi.fn()
mockedUseLayoutMutations.mockReturnValue({
setSource: mockSetSource,
bringNodeToFront: mockBringNodeToFront
} as Partial<ReturnType<typeof useLayoutMutations>> as ReturnType<
typeof useLayoutMutations
>)
const { bringNodeToFront } = useNodeZIndex()
bringNodeToFront('node2', LayoutSource.Canvas)
expect(mockSetSource).toHaveBeenCalledWith(LayoutSource.Canvas)
expect(mockBringNodeToFront).toHaveBeenCalledWith('node2')
})
it('should use custom layout source from options', () => {
const mockSetSource = vi.fn()
const mockBringNodeToFront = vi.fn()
mockedUseLayoutMutations.mockReturnValue({
setSource: mockSetSource,
bringNodeToFront: mockBringNodeToFront
} as Partial<ReturnType<typeof useLayoutMutations>> as ReturnType<
typeof useLayoutMutations
>)
const { bringNodeToFront } = useNodeZIndex({
layoutSource: LayoutSource.External
})
bringNodeToFront('node3')
expect(mockSetSource).toHaveBeenCalledWith(LayoutSource.External)
expect(mockBringNodeToFront).toHaveBeenCalledWith('node3')
})
it('should override layout source with explicit source parameter', () => {
const mockSetSource = vi.fn()
const mockBringNodeToFront = vi.fn()
mockedUseLayoutMutations.mockReturnValue({
setSource: mockSetSource,
bringNodeToFront: mockBringNodeToFront
} as Partial<ReturnType<typeof useLayoutMutations>> as ReturnType<
typeof useLayoutMutations
>)
const { bringNodeToFront } = useNodeZIndex({
layoutSource: LayoutSource.External
})
bringNodeToFront('node4', LayoutSource.Canvas)
expect(mockSetSource).toHaveBeenCalledWith(LayoutSource.Canvas)
expect(mockBringNodeToFront).toHaveBeenCalledWith('node4')
})
})
})