diff --git a/src/renderer/extensions/vueNodes/components/ImagePreview.spec.ts b/src/renderer/extensions/vueNodes/components/ImagePreview.spec.ts deleted file mode 100644 index 44fbccf2ae..0000000000 --- a/src/renderer/extensions/vueNodes/components/ImagePreview.spec.ts +++ /dev/null @@ -1,265 +0,0 @@ -import { mount } from '@vue/test-utils' -import { describe, expect, it, vi } from 'vitest' -import { nextTick } from 'vue' - -import ImagePreview from './ImagePreview.vue' - -describe('ImagePreview', () => { - const defaultProps = { - imageUrls: [ - '/api/view?filename=test1.png&type=output', - '/api/view?filename=test2.png&type=output' - ], - dimensions: '512 x 512' - } - - const mountImagePreview = (props = {}) => { - return mount(ImagePreview, { - props: { ...defaultProps, ...props } - }) - } - - it('renders image preview when imageUrls provided', () => { - const wrapper = mountImagePreview() - - expect(wrapper.find('.image-preview').exists()).toBe(true) - expect(wrapper.find('img').exists()).toBe(true) - expect(wrapper.find('img').attributes('src')).toBe( - defaultProps.imageUrls[0] - ) - }) - - it('does not render when no imageUrls provided', () => { - const wrapper = mountImagePreview({ imageUrls: [] }) - - expect(wrapper.find('.image-preview').exists()).toBe(false) - }) - - it('displays dimensions correctly', () => { - const wrapper = mountImagePreview({ dimensions: '1024 x 768' }) - - expect(wrapper.text()).toContain('1024 x 768') - }) - - it('shows navigation dots for multiple images', () => { - const wrapper = mountImagePreview() - - const navigationDots = wrapper.findAll('.w-2.h-2.rounded-full') - expect(navigationDots).toHaveLength(2) - }) - - it('does not show navigation dots for single image', () => { - const wrapper = mountImagePreview({ - imageUrls: [defaultProps.imageUrls[0]] - }) - - const navigationDots = wrapper.findAll('.w-2.h-2.rounded-full') - expect(navigationDots).toHaveLength(0) - }) - - it('shows action buttons on hover', async () => { - const wrapper = mountImagePreview() - - // Initially buttons should not be visible - expect(wrapper.find('.actions').exists()).toBe(false) - - // Trigger hover - await wrapper.trigger('mouseenter') - await nextTick() - - // Action buttons should now be visible - expect(wrapper.find('.actions').exists()).toBe(true) - expect(wrapper.findAll('.action-btn')).toHaveLength(3) // mask, download, remove - }) - - it('hides action buttons when not hovering', async () => { - const wrapper = mountImagePreview() - - // Trigger hover - await wrapper.trigger('mouseenter') - await nextTick() - expect(wrapper.find('.actions').exists()).toBe(true) - - // Trigger mouse leave - await wrapper.trigger('mouseleave') - await nextTick() - expect(wrapper.find('.actions').exists()).toBe(false) - }) - - it('shows mask/edit button only for single images', async () => { - // Multiple images - should not show mask button - const multipleImagesWrapper = mountImagePreview() - await multipleImagesWrapper.trigger('mouseenter') - await nextTick() - - const maskButtonMultiple = multipleImagesWrapper.find('[title="Edit/Mask"]') - expect(maskButtonMultiple.exists()).toBe(false) - - // Single image - should show mask button - const singleImageWrapper = mountImagePreview({ - imageUrls: [defaultProps.imageUrls[0]] - }) - await singleImageWrapper.trigger('mouseenter') - await nextTick() - - const maskButtonSingle = singleImageWrapper.find('[title="Edit/Mask"]') - expect(maskButtonSingle.exists()).toBe(true) - }) - - it('handles action button clicks', async () => { - const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => {}) - const wrapper = mountImagePreview({ - imageUrls: [defaultProps.imageUrls[0]] - }) - - await wrapper.trigger('mouseenter') - await nextTick() - - // Test Edit/Mask button - await wrapper.find('[title="Edit/Mask"]').trigger('click') - expect(consoleSpy).toHaveBeenCalledWith( - 'Edit/Mask clicked for:', - defaultProps.imageUrls[0] - ) - - // Test Remove button - await wrapper.find('[title="Remove"]').trigger('click') - expect(consoleSpy).toHaveBeenCalledWith( - 'Remove clicked for:', - defaultProps.imageUrls[0] - ) - - consoleSpy.mockRestore() - }) - - it('handles download button click', async () => { - // Mock DOM methods for download test - const mockLink = { - href: '', - download: '', - click: vi.fn() - } - const mockCreateElement = vi - .spyOn(document, 'createElement') - .mockReturnValue(mockLink as any) - const mockAppendChild = vi - .spyOn(document.body, 'appendChild') - .mockImplementation(() => mockLink as any) - const mockRemoveChild = vi - .spyOn(document.body, 'removeChild') - .mockImplementation(() => mockLink as any) - - const wrapper = mountImagePreview({ - imageUrls: [defaultProps.imageUrls[0]] - }) - - await wrapper.trigger('mouseenter') - await nextTick() - - // Test Download button - await wrapper.find('[title="Download"]').trigger('click') - - expect(mockCreateElement).toHaveBeenCalledWith('a') - expect(mockLink.href).toBe(defaultProps.imageUrls[0]) - expect(mockLink.click).toHaveBeenCalled() - - mockCreateElement.mockRestore() - mockAppendChild.mockRestore() - mockRemoveChild.mockRestore() - }) - - it('switches images when navigation dots are clicked', async () => { - const wrapper = mountImagePreview() - - // Initially shows first image - expect(wrapper.find('img').attributes('src')).toBe( - defaultProps.imageUrls[0] - ) - - // Click second navigation dot - const navigationDots = wrapper.findAll('.w-2.h-2.rounded-full') - await navigationDots[1].trigger('click') - await nextTick() - - // Should now show second image - expect(wrapper.find('img').attributes('src')).toBe( - defaultProps.imageUrls[1] - ) - }) - - it('applies correct classes to navigation dots based on current image', async () => { - const wrapper = mountImagePreview() - - const navigationDots = wrapper.findAll('.w-2.h-2.rounded-full') - - // First dot should be active (has bg-white class) - expect(navigationDots[0].classes()).toContain('bg-white') - expect(navigationDots[1].classes()).toContain('bg-white/50') - - // Switch to second image - await navigationDots[1].trigger('click') - await nextTick() - - // Second dot should now be active - expect(navigationDots[0].classes()).toContain('bg-white/50') - expect(navigationDots[1].classes()).toContain('bg-white') - }) - - it('updates dimensions when image loads', async () => { - const wrapper = mountImagePreview({ dimensions: undefined }) - - // Initially shows "Loading..." - expect(wrapper.text()).toContain('Loading...') - - // Simulate image load event - const img = wrapper.find('img') - const mockLoadEvent = { - target: { - naturalWidth: 1024, - naturalHeight: 768 - } - } - await img.trigger('load', mockLoadEvent) - await nextTick() - - // Should now show actual dimensions - expect(wrapper.text()).toContain('1024 x 768') - }) - - it('handles image load errors gracefully', async () => { - const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {}) - const wrapper = mountImagePreview() - - const img = wrapper.find('img') - await img.trigger('error') - - expect(consoleSpy).toHaveBeenCalledWith( - 'Failed to load image:', - defaultProps.imageUrls[0] - ) - - consoleSpy.mockRestore() - }) - - it('has proper accessibility attributes', () => { - const wrapper = mountImagePreview() - - const img = wrapper.find('img') - expect(img.attributes('alt')).toBe('Node output 1') - }) - - it('updates alt text when switching images', async () => { - const wrapper = mountImagePreview() - - // Initially first image - expect(wrapper.find('img').attributes('alt')).toBe('Node output 1') - - // Switch to second image - const navigationDots = wrapper.findAll('.w-2.h-2.rounded-full') - await navigationDots[1].trigger('click') - await nextTick() - - // Alt text should update - expect(wrapper.find('img').attributes('alt')).toBe('Node output 2') - }) -}) diff --git a/tests-ui/tests/renderer/extensions/vueNodes/composables/useImagePreview.test.ts b/tests-ui/tests/renderer/extensions/vueNodes/composables/useImagePreview.test.ts index 7837c9f89d..542867d82c 100644 --- a/tests-ui/tests/renderer/extensions/vueNodes/composables/useImagePreview.test.ts +++ b/tests-ui/tests/renderer/extensions/vueNodes/composables/useImagePreview.test.ts @@ -1,8 +1,29 @@ -import { describe, expect, it, vi } from 'vitest' +import { createPinia, setActivePinia } from 'pinia' +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { useImagePreview } from '@/renderer/extensions/vueNodes/composables/useImagePreview' +vi.mock('@/stores/commandStore', () => ({ + useCommandStore: vi.fn(() => ({ + execute: vi.fn().mockResolvedValue(undefined) + })) +})) + +vi.mock('@/stores/imagePreviewStore', () => ({ + useNodeOutputStore: vi.fn(() => ({ + removeNodeOutputs: vi.fn().mockReturnValue(true) + })) +})) + describe('useImagePreview', () => { + beforeEach(() => { + vi.clearAllMocks() + setActivePinia(createPinia()) + }) + + afterEach(() => { + vi.restoreAllMocks() + }) const mockImageUrls = [ '/api/view?filename=test1.png&type=output', '/api/view?filename=test2.png&type=output', @@ -14,13 +35,26 @@ describe('useImagePreview', () => { naturalWidth: number, naturalHeight: number ): Event => { - const mockImg = { - naturalWidth, - naturalHeight, + // Create a mock that satisfies HTMLImageElement type checking + const mockImg = Object.create(HTMLImageElement.prototype) + + // Define naturalWidth and naturalHeight as properties since they're readonly on HTMLImageElement + Object.defineProperty(mockImg, 'naturalWidth', { + value: naturalWidth, + writable: false, + configurable: true + }) + Object.defineProperty(mockImg, 'naturalHeight', { + value: naturalHeight, + writable: false, + configurable: true + }) + + Object.assign(mockImg, { addEventListener: vi.fn(), dispatchEvent: vi.fn(), removeEventListener: vi.fn() - } satisfies EventTarget & { naturalWidth: number; naturalHeight: number } + }) return { target: mockImg, @@ -100,19 +134,6 @@ describe('useImagePreview', () => { expect(currentIndex.value).toBe(2) // Should remain unchanged }) - it('handles edit mask action', () => { - const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => {}) - const { handleEditMask } = useImagePreview(mockImageUrls) - - handleEditMask() - expect(consoleSpy).toHaveBeenCalledWith( - 'Edit/Mask clicked for:', - mockImageUrls[0] - ) - - consoleSpy.mockRestore() - }) - it('handles download action', () => { // Mock DOM methods const mockLink = { @@ -122,13 +143,13 @@ describe('useImagePreview', () => { } const mockCreateElement = vi .spyOn(document, 'createElement') - .mockReturnValue(mockLink as any) + .mockReturnValue(mockLink as unknown as HTMLAnchorElement) const mockAppendChild = vi .spyOn(document.body, 'appendChild') - .mockImplementation(() => mockLink as any) + .mockImplementation(() => mockLink as unknown as HTMLAnchorElement) const mockRemoveChild = vi .spyOn(document.body, 'removeChild') - .mockImplementation(() => mockLink as any) + .mockImplementation(() => mockLink as unknown as HTMLAnchorElement) const { handleDownload } = useImagePreview(mockImageUrls) handleDownload() @@ -144,19 +165,6 @@ describe('useImagePreview', () => { mockRemoveChild.mockRestore() }) - it('handles remove action', () => { - const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => {}) - const { handleRemove } = useImagePreview(mockImageUrls) - - handleRemove() - expect(consoleSpy).toHaveBeenCalledWith( - 'Remove clicked for:', - mockImageUrls[0] - ) - - consoleSpy.mockRestore() - }) - it('handles image load event correctly', () => { const { handleImageLoad, actualDimensions } = useImagePreview(mockImageUrls) @@ -188,16 +196,4 @@ describe('useImagePreview', () => { setCurrentIndex(1) expect(actualDimensions.value).toBeNull() }) - - it('composable functions are properly defined', () => { - const { handleEditMask, handleDownload, handleRemove } = - useImagePreview(mockImageUrls) - - expect(handleEditMask).toBeDefined() - expect(typeof handleEditMask).toBe('function') - expect(handleDownload).toBeDefined() - expect(typeof handleDownload).toBe('function') - expect(handleRemove).toBeDefined() - expect(typeof handleRemove).toBe('function') - }) })