mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-05-03 20:51:58 +00:00
test: migrate 8 hard-case component tests from VTU to VTL (Phase 3) (#10493)
## Summary Phase 3 of the VTL migration: migrate 8 hard-case component tests from @vue/test-utils to @testing-library/vue (68 tests). Stacked on #10490. ## Changes - **What**: Migrate SignInForm, CurrentUserButton, NodeSearchBoxPopover, BaseThumbnail, JobAssetsList, SelectionToolbox, QueueOverlayExpanded, PackVersionSelectorPopover from VTU to VTL - **`wrapper.vm` elimination**: 13 instances across 4 files (5 in SignInForm, 3 in CurrentUserButton, 3 in PackVersionSelectorPopover, 2 in BaseThumbnail) replaced with user interactions or removed - **`vm.$emit()` on stubs**: Interactive stubs with `setup(_, { emit })` expose buttons or closure-based emit functions (QueueOverlayExpanded, NodeSearchBoxPopover, JobAssetsList) - **Removed**: 6 change-detector/redundant tests, 3 `@ts-expect-error` annotations, `PackVersionSelectorVM` interface, `getVM` helper - **BaseThumbnail**: Removed `useEventListener` mock — real event handler attaches, `fireEvent.error(img)` triggers error state ## Review Focus - Interactive stub patterns: `JobAssetsListStub` and `NodeSearchBoxStub` use closure-based emit functions to trigger parent event handlers without `vm.$emit` - SignInForm form submission test fills PrimeVue Form fields via `userEvent.type` and submits via button click (replaces `vm.onSubmit()` direct call) - CurrentUserButton Popover stub tracks open/close state reactively - JobAssetsList: file-level `eslint-disable` for `no-container`/`no-node-access`/`prefer-user-event` since stubs lack ARIA roles and hover tests need `fireEvent` ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-10493-test-migrate-8-hard-case-component-tests-from-VTU-to-VTL-Phase-3-32e6d73d365081f88097df634606d7e3) by [Unito](https://www.unito.io) --------- Co-authored-by: Amp <amp@ampcode.com>
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { mount } from '@vue/test-utils'
|
||||
/* eslint-disable testing-library/no-container, testing-library/no-node-access */
|
||||
import { fireEvent, render } from '@testing-library/vue'
|
||||
import { createPinia, setActivePinia } from 'pinia'
|
||||
import PrimeVue from 'primevue/config'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
@@ -129,6 +130,10 @@ describe('SelectionToolbox', () => {
|
||||
beforeEach(() => {
|
||||
setActivePinia(createPinia())
|
||||
canvasStore = useCanvasStore()
|
||||
nodeDefMock = {
|
||||
type: 'TestNode',
|
||||
title: 'Test Node'
|
||||
} as unknown
|
||||
|
||||
// Mock the canvas to avoid "getCanvas: canvas is null" errors
|
||||
canvasStore.canvas = createMockCanvas()
|
||||
@@ -136,8 +141,8 @@ describe('SelectionToolbox', () => {
|
||||
vi.resetAllMocks()
|
||||
})
|
||||
|
||||
const mountComponent = (props = {}) => {
|
||||
return mount(SelectionToolbox, {
|
||||
function renderComponent(props = {}): { container: Element } {
|
||||
const { container } = render(SelectionToolbox, {
|
||||
props,
|
||||
global: {
|
||||
plugins: [i18n, PrimeVue],
|
||||
@@ -169,7 +174,9 @@ describe('SelectionToolbox', () => {
|
||||
Load3DViewerButton: {
|
||||
template: '<div class="load-3d-viewer-button" />'
|
||||
},
|
||||
MaskEditorButton: { template: '<div class="mask-editor-button" />' },
|
||||
MaskEditorButton: {
|
||||
template: '<div class="mask-editor-button" />'
|
||||
},
|
||||
DeleteButton: {
|
||||
template:
|
||||
'<button data-testid="delete-button" class="delete-button" />'
|
||||
@@ -193,6 +200,7 @@ describe('SelectionToolbox', () => {
|
||||
}
|
||||
}
|
||||
})
|
||||
return { container }
|
||||
}
|
||||
|
||||
describe('Button Visibility Logic', () => {
|
||||
@@ -204,91 +212,91 @@ describe('SelectionToolbox', () => {
|
||||
it('should show info button only for single selections', () => {
|
||||
// Single node selection
|
||||
canvasStore.selectedItems = [createMockPositionable()]
|
||||
const wrapper = mountComponent()
|
||||
expect(wrapper.find('.info-button').exists()).toBe(true)
|
||||
const { container } = renderComponent()
|
||||
expect(container.querySelector('.info-button')).toBeTruthy()
|
||||
|
||||
// Multiple node selection
|
||||
// Multiple node selection - render in separate test scope
|
||||
canvasStore.selectedItems = [
|
||||
createMockPositionable(),
|
||||
createMockPositionable()
|
||||
]
|
||||
wrapper.unmount()
|
||||
const wrapper2 = mountComponent()
|
||||
expect(wrapper2.find('.info-button').exists()).toBe(false)
|
||||
const { container: container2 } = renderComponent()
|
||||
expect(container2.querySelector('.info-button')).toBeFalsy()
|
||||
})
|
||||
|
||||
it('should not show info button when node definition is not found', () => {
|
||||
canvasStore.selectedItems = [createMockPositionable()]
|
||||
// mock nodedef and return null
|
||||
nodeDefMock = null
|
||||
// remount component
|
||||
const wrapper = mountComponent()
|
||||
expect(wrapper.find('.info-button').exists()).toBe(false)
|
||||
const { container } = renderComponent()
|
||||
expect(container.querySelector('.info-button')).toBeFalsy()
|
||||
})
|
||||
|
||||
it('should show color picker for all selections', () => {
|
||||
// Single node selection
|
||||
canvasStore.selectedItems = [createMockPositionable()]
|
||||
const wrapper = mountComponent()
|
||||
expect(wrapper.find('[data-testid="color-picker-button"]').exists()).toBe(
|
||||
true
|
||||
)
|
||||
const { container } = renderComponent()
|
||||
expect(
|
||||
container.querySelector('[data-testid="color-picker-button"]')
|
||||
).toBeTruthy()
|
||||
|
||||
// Multiple node selection
|
||||
canvasStore.selectedItems = [
|
||||
createMockPositionable(),
|
||||
createMockPositionable()
|
||||
]
|
||||
wrapper.unmount()
|
||||
const wrapper2 = mountComponent()
|
||||
const { container: container2 } = renderComponent()
|
||||
expect(
|
||||
wrapper2.find('[data-testid="color-picker-button"]').exists()
|
||||
).toBe(true)
|
||||
container2.querySelector('[data-testid="color-picker-button"]')
|
||||
).toBeTruthy()
|
||||
})
|
||||
|
||||
it('should show frame nodes only for multiple selections', () => {
|
||||
// Single node selection
|
||||
canvasStore.selectedItems = [createMockPositionable()]
|
||||
const wrapper = mountComponent()
|
||||
expect(wrapper.find('.frame-nodes').exists()).toBe(false)
|
||||
const { container } = renderComponent()
|
||||
expect(container.querySelector('.frame-nodes')).toBeFalsy()
|
||||
|
||||
// Multiple node selection
|
||||
canvasStore.selectedItems = [
|
||||
createMockPositionable(),
|
||||
createMockPositionable()
|
||||
]
|
||||
wrapper.unmount()
|
||||
const wrapper2 = mountComponent()
|
||||
expect(wrapper2.find('.frame-nodes').exists()).toBe(true)
|
||||
const { container: container2 } = renderComponent()
|
||||
expect(container2.querySelector('.frame-nodes')).toBeTruthy()
|
||||
})
|
||||
|
||||
it('should show bypass button for appropriate selections', () => {
|
||||
// Single node selection
|
||||
canvasStore.selectedItems = [createMockPositionable()]
|
||||
const wrapper = mountComponent()
|
||||
expect(wrapper.find('[data-testid="bypass-button"]').exists()).toBe(true)
|
||||
const { container } = renderComponent()
|
||||
expect(
|
||||
container.querySelector('[data-testid="bypass-button"]')
|
||||
).toBeTruthy()
|
||||
|
||||
// Multiple node selection
|
||||
canvasStore.selectedItems = [
|
||||
createMockPositionable(),
|
||||
createMockPositionable()
|
||||
]
|
||||
wrapper.unmount()
|
||||
const wrapper2 = mountComponent()
|
||||
expect(wrapper2.find('[data-testid="bypass-button"]').exists()).toBe(true)
|
||||
const { container: container2 } = renderComponent()
|
||||
expect(
|
||||
container2.querySelector('[data-testid="bypass-button"]')
|
||||
).toBeTruthy()
|
||||
})
|
||||
|
||||
it('should show common buttons for all selections', () => {
|
||||
canvasStore.selectedItems = [createMockPositionable()]
|
||||
const wrapper = mountComponent()
|
||||
const { container } = renderComponent()
|
||||
|
||||
expect(wrapper.find('[data-testid="delete-button"]').exists()).toBe(true)
|
||||
expect(
|
||||
wrapper.find('[data-testid="convert-to-subgraph-button"]').exists()
|
||||
).toBe(true)
|
||||
expect(wrapper.find('[data-testid="more-options-button"]').exists()).toBe(
|
||||
true
|
||||
)
|
||||
container.querySelector('[data-testid="delete-button"]')
|
||||
).toBeTruthy()
|
||||
expect(
|
||||
container.querySelector('[data-testid="convert-to-subgraph-button"]')
|
||||
).toBeTruthy()
|
||||
expect(
|
||||
container.querySelector('[data-testid="more-options-button"]')
|
||||
).toBeTruthy()
|
||||
})
|
||||
|
||||
it('should show mask editor only for single image nodes', () => {
|
||||
@@ -297,15 +305,14 @@ describe('SelectionToolbox', () => {
|
||||
// Single image node
|
||||
isImageNodeSpy.mockReturnValue(true)
|
||||
canvasStore.selectedItems = [createMockPositionable()]
|
||||
const wrapper = mountComponent()
|
||||
expect(wrapper.find('.mask-editor-button').exists()).toBe(true)
|
||||
const { container } = renderComponent()
|
||||
expect(container.querySelector('.mask-editor-button')).toBeTruthy()
|
||||
|
||||
// Single non-image node
|
||||
isImageNodeSpy.mockReturnValue(false)
|
||||
canvasStore.selectedItems = [createMockPositionable()]
|
||||
wrapper.unmount()
|
||||
const wrapper2 = mountComponent()
|
||||
expect(wrapper2.find('.mask-editor-button').exists()).toBe(false)
|
||||
const { container: container2 } = renderComponent()
|
||||
expect(container2.querySelector('.mask-editor-button')).toBeFalsy()
|
||||
})
|
||||
|
||||
it('should show Color picker button only for single Load3D nodes', () => {
|
||||
@@ -314,15 +321,14 @@ describe('SelectionToolbox', () => {
|
||||
// Single Load3D node
|
||||
isLoad3dNodeSpy.mockReturnValue(true)
|
||||
canvasStore.selectedItems = [createMockPositionable()]
|
||||
const wrapper = mountComponent()
|
||||
expect(wrapper.find('.load-3d-viewer-button').exists()).toBe(true)
|
||||
const { container } = renderComponent()
|
||||
expect(container.querySelector('.load-3d-viewer-button')).toBeTruthy()
|
||||
|
||||
// Single non-Load3D node
|
||||
isLoad3dNodeSpy.mockReturnValue(false)
|
||||
canvasStore.selectedItems = [createMockPositionable()]
|
||||
wrapper.unmount()
|
||||
const wrapper2 = mountComponent()
|
||||
expect(wrapper2.find('.load-3d-viewer-button').exists()).toBe(false)
|
||||
const { container: container2 } = renderComponent()
|
||||
expect(container2.querySelector('.load-3d-viewer-button')).toBeFalsy()
|
||||
})
|
||||
|
||||
it('should show ExecuteButton only when output nodes are selected', () => {
|
||||
@@ -335,22 +341,20 @@ describe('SelectionToolbox', () => {
|
||||
{ type: 'SaveImage' }
|
||||
] as LGraphNode[])
|
||||
canvasStore.selectedItems = [createMockPositionable()]
|
||||
const wrapper = mountComponent()
|
||||
expect(wrapper.find('.execute-button').exists()).toBe(true)
|
||||
const { container } = renderComponent()
|
||||
expect(container.querySelector('.execute-button')).toBeTruthy()
|
||||
|
||||
// Without output node selected
|
||||
isOutputNodeSpy.mockReturnValue(false)
|
||||
filterOutputNodesSpy.mockReturnValue([])
|
||||
canvasStore.selectedItems = [createMockPositionable()]
|
||||
wrapper.unmount()
|
||||
const wrapper2 = mountComponent()
|
||||
expect(wrapper2.find('.execute-button').exists()).toBe(false)
|
||||
const { container: container2 } = renderComponent()
|
||||
expect(container2.querySelector('.execute-button')).toBeFalsy()
|
||||
|
||||
// No selection at all
|
||||
canvasStore.selectedItems = []
|
||||
wrapper2.unmount()
|
||||
const wrapper3 = mountComponent()
|
||||
expect(wrapper3.find('.execute-button').exists()).toBe(false)
|
||||
const { container: container3 } = renderComponent()
|
||||
expect(container3.querySelector('.execute-button')).toBeFalsy()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -358,19 +362,20 @@ describe('SelectionToolbox', () => {
|
||||
it('should show dividers between button groups when both groups have buttons', () => {
|
||||
// Setup single node to show info + other buttons
|
||||
canvasStore.selectedItems = [createMockPositionable()]
|
||||
const wrapper = mountComponent()
|
||||
const { container } = renderComponent()
|
||||
|
||||
const dividers = wrapper.findAll('.vertical-divider')
|
||||
const dividers = container.querySelectorAll('.vertical-divider')
|
||||
expect(dividers.length).toBeGreaterThan(0)
|
||||
})
|
||||
|
||||
it('should not show dividers when adjacent groups are empty', () => {
|
||||
// No selection should show minimal buttons and dividers
|
||||
canvasStore.selectedItems = []
|
||||
const wrapper = mountComponent()
|
||||
const { container } = renderComponent()
|
||||
|
||||
const buttons = wrapper.find('.panel').element.children
|
||||
expect(buttons.length).toBeGreaterThan(0) // At least MoreOptions should show
|
||||
expect(
|
||||
container.querySelector('[data-testid="more-options-button"]')
|
||||
).toBeTruthy()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -390,9 +395,9 @@ describe('SelectionToolbox', () => {
|
||||
} as ReturnType<typeof useExtensionService>)
|
||||
|
||||
canvasStore.selectedItems = [createMockPositionable()]
|
||||
const wrapper = mountComponent()
|
||||
const { container } = renderComponent()
|
||||
|
||||
expect(wrapper.find('.extension-command-button').exists()).toBe(true)
|
||||
expect(container.querySelector('.extension-command-button')).toBeTruthy()
|
||||
})
|
||||
|
||||
it('should not render extension commands when none available', () => {
|
||||
@@ -400,47 +405,9 @@ describe('SelectionToolbox', () => {
|
||||
mockExtensionService.mockReturnValue(createMockExtensionService())
|
||||
|
||||
canvasStore.selectedItems = [createMockPositionable()]
|
||||
const wrapper = mountComponent()
|
||||
const { container } = renderComponent()
|
||||
|
||||
expect(wrapper.find('.extension-command-button').exists()).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('Container Styling', () => {
|
||||
it('should apply minimap container styles', () => {
|
||||
const mockExtensionService = vi.mocked(useExtensionService)
|
||||
mockExtensionService.mockReturnValue(createMockExtensionService())
|
||||
|
||||
canvasStore.selectedItems = [createMockPositionable()]
|
||||
const wrapper = mountComponent()
|
||||
|
||||
const panel = wrapper.find('.panel')
|
||||
expect(panel.exists()).toBe(true)
|
||||
})
|
||||
|
||||
it('should have correct CSS classes', () => {
|
||||
const mockExtensionService = vi.mocked(useExtensionService)
|
||||
mockExtensionService.mockReturnValue(createMockExtensionService())
|
||||
|
||||
canvasStore.selectedItems = [createMockPositionable()]
|
||||
const wrapper = mountComponent()
|
||||
|
||||
const panel = wrapper.find('.panel')
|
||||
expect(panel.classes()).toContain('selection-toolbox')
|
||||
expect(panel.classes()).toContain('absolute')
|
||||
expect(panel.classes()).toContain('left-1/2')
|
||||
expect(panel.classes()).toContain('rounded-lg')
|
||||
})
|
||||
|
||||
it('should handle animation class conditionally', () => {
|
||||
const mockExtensionService = vi.mocked(useExtensionService)
|
||||
mockExtensionService.mockReturnValue(createMockExtensionService())
|
||||
|
||||
canvasStore.selectedItems = [createMockPositionable()]
|
||||
const wrapper = mountComponent()
|
||||
|
||||
const panel = wrapper.find('.panel')
|
||||
expect(panel.exists()).toBe(true)
|
||||
expect(container.querySelector('.extension-command-button')).toBeFalsy()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -461,10 +428,11 @@ describe('SelectionToolbox', () => {
|
||||
mockExtensionService.mockReturnValue(createMockExtensionService())
|
||||
|
||||
canvasStore.selectedItems = [createMockPositionable()]
|
||||
const wrapper = mountComponent()
|
||||
const { container } = renderComponent()
|
||||
|
||||
const panel = wrapper.find('.panel')
|
||||
await panel.trigger('wheel')
|
||||
const panel = container.querySelector('.panel')
|
||||
expect(panel).toBeTruthy()
|
||||
await fireEvent.wheel(panel!)
|
||||
|
||||
expect(forwardEventToCanvasSpy).toHaveBeenCalled()
|
||||
})
|
||||
@@ -478,12 +446,12 @@ describe('SelectionToolbox', () => {
|
||||
|
||||
it('should hide most buttons when no items selected', () => {
|
||||
canvasStore.selectedItems = []
|
||||
const wrapper = mountComponent()
|
||||
const { container } = renderComponent()
|
||||
|
||||
expect(wrapper.find('.info-button').exists()).toBe(false)
|
||||
expect(wrapper.find('.color-picker-button').exists()).toBe(false)
|
||||
expect(wrapper.find('.frame-nodes').exists()).toBe(false)
|
||||
expect(wrapper.find('.bookmark-button').exists()).toBe(false)
|
||||
expect(container.querySelector('.info-button')).toBeFalsy()
|
||||
expect(container.querySelector('.color-picker-button')).toBeFalsy()
|
||||
expect(container.querySelector('.frame-nodes')).toBeFalsy()
|
||||
expect(container.querySelector('.bookmark-button')).toBeFalsy()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user