mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-29 10:42:44 +00:00
Add tests for ChatHistoryWidget and related features (#3921)
This commit is contained in:
95
tests-ui/tests/components/ChatHistoryWidget.spec.ts
Normal file
95
tests-ui/tests/components/ChatHistoryWidget.spec.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
import { mount } from '@vue/test-utils'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import { createI18n } from 'vue-i18n'
|
||||
|
||||
import ChatHistoryWidget from '@/components/graph/widgets/ChatHistoryWidget.vue'
|
||||
|
||||
const i18n = createI18n({
|
||||
legacy: false,
|
||||
locale: 'en',
|
||||
messages: {
|
||||
en: {
|
||||
g: { edit: 'Edit' },
|
||||
chatHistory: {
|
||||
cancelEdit: 'Cancel edit',
|
||||
cancelEditTooltip: 'Cancel edit'
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
vi.mock('@/components/graph/widgets/chatHistory/CopyButton.vue', () => ({
|
||||
default: {
|
||||
name: 'CopyButton',
|
||||
template: '<div class="mock-copy-button"></div>',
|
||||
props: ['text']
|
||||
}
|
||||
}))
|
||||
|
||||
vi.mock('@/components/graph/widgets/chatHistory/ResponseBlurb.vue', () => ({
|
||||
default: {
|
||||
name: 'ResponseBlurb',
|
||||
template: '<div class="mock-response-blurb"><slot /></div>',
|
||||
props: ['text']
|
||||
}
|
||||
}))
|
||||
|
||||
describe('ChatHistoryWidget.vue', () => {
|
||||
const mockHistory = JSON.stringify([
|
||||
{ prompt: 'Test prompt', response: 'Test response', response_id: '123' }
|
||||
])
|
||||
|
||||
const mountWidget = (props: { history: string; widget?: any }) => {
|
||||
return mount(ChatHistoryWidget, {
|
||||
props,
|
||||
global: {
|
||||
plugins: [i18n],
|
||||
stubs: {
|
||||
Button: {
|
||||
template: '<button><slot /></button>',
|
||||
props: ['icon', 'aria-label']
|
||||
},
|
||||
ScrollPanel: { template: '<div><slot /></div>' }
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
it('renders chat history correctly', () => {
|
||||
const wrapper = mountWidget({ history: mockHistory })
|
||||
expect(wrapper.text()).toContain('Test prompt')
|
||||
expect(wrapper.text()).toContain('Test response')
|
||||
})
|
||||
|
||||
it('handles empty history', () => {
|
||||
const wrapper = mountWidget({ history: '[]' })
|
||||
expect(wrapper.find('.mb-4').exists()).toBe(false)
|
||||
})
|
||||
|
||||
it('edits previous prompts', () => {
|
||||
const mockWidget = {
|
||||
node: { widgets: [{ name: 'prompt', value: '' }] }
|
||||
}
|
||||
|
||||
const wrapper = mountWidget({ history: mockHistory, widget: mockWidget })
|
||||
const vm = wrapper.vm as any
|
||||
vm.handleEdit(0)
|
||||
|
||||
expect(mockWidget.node.widgets[0].value).toContain('Test prompt')
|
||||
expect(mockWidget.node.widgets[0].value).toContain('starting_point_id')
|
||||
})
|
||||
|
||||
it('cancels editing correctly', () => {
|
||||
const mockWidget = {
|
||||
node: { widgets: [{ name: 'prompt', value: 'Original value' }] }
|
||||
}
|
||||
|
||||
const wrapper = mountWidget({ history: mockHistory, widget: mockWidget })
|
||||
const vm = wrapper.vm as any
|
||||
|
||||
vm.handleEdit(0)
|
||||
vm.handleCancelEdit()
|
||||
|
||||
expect(mockWidget.node.widgets[0].value).toBe('Original value')
|
||||
})
|
||||
})
|
||||
63
tests-ui/tests/composables/useNodeChatHistory.test.ts
Normal file
63
tests-ui/tests/composables/useNodeChatHistory.test.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import { LGraphNode } from '@comfyorg/litegraph'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import { useNodeChatHistory } from '@/composables/node/useNodeChatHistory'
|
||||
|
||||
vi.mock('@/composables/widgets/useChatHistoryWidget', () => ({
|
||||
useChatHistoryWidget: () => {
|
||||
return (node: any, inputSpec: any) => {
|
||||
const widget = {
|
||||
name: inputSpec.name,
|
||||
type: inputSpec.type
|
||||
}
|
||||
|
||||
if (!node.widgets) {
|
||||
node.widgets = []
|
||||
}
|
||||
node.widgets.push(widget)
|
||||
|
||||
return widget
|
||||
}
|
||||
}
|
||||
}))
|
||||
|
||||
// Mock LGraphNode type
|
||||
type MockNode = {
|
||||
widgets: Array<{ name: string; type: string }>
|
||||
setDirtyCanvas: ReturnType<typeof vi.fn>
|
||||
addCustomWidget: ReturnType<typeof vi.fn>
|
||||
[key: string]: any
|
||||
}
|
||||
|
||||
describe('useNodeChatHistory', () => {
|
||||
const mockNode = {
|
||||
widgets: [],
|
||||
setDirtyCanvas: vi.fn(),
|
||||
addCustomWidget: vi.fn()
|
||||
} as unknown as LGraphNode & MockNode
|
||||
|
||||
beforeEach(() => {
|
||||
mockNode.widgets = []
|
||||
mockNode.setDirtyCanvas.mockClear()
|
||||
mockNode.addCustomWidget.mockClear()
|
||||
})
|
||||
|
||||
it('adds chat history widget to node', () => {
|
||||
const { showChatHistory } = useNodeChatHistory()
|
||||
showChatHistory(mockNode)
|
||||
|
||||
expect(mockNode.widgets.length).toBe(1)
|
||||
expect(mockNode.widgets[0].name).toBe('$$node-chat-history')
|
||||
expect(mockNode.setDirtyCanvas).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('removes chat history widget from node', () => {
|
||||
const { showChatHistory, removeChatHistory } = useNodeChatHistory()
|
||||
showChatHistory(mockNode)
|
||||
|
||||
expect(mockNode.widgets.length).toBe(1)
|
||||
|
||||
removeChatHistory(mockNode)
|
||||
expect(mockNode.widgets.length).toBe(0)
|
||||
})
|
||||
})
|
||||
82
tests-ui/tests/store/executionStore.test.ts
Normal file
82
tests-ui/tests/store/executionStore.test.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
import { createPinia, setActivePinia } from 'pinia'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import { useExecutionStore } from '@/stores/executionStore'
|
||||
|
||||
// Remove any previous global types
|
||||
declare global {
|
||||
// Empty interface to override any previous declarations
|
||||
interface Window {}
|
||||
}
|
||||
|
||||
const mockShowChatHistory = vi.fn()
|
||||
vi.mock('@/composables/node/useNodeChatHistory', () => ({
|
||||
useNodeChatHistory: () => ({
|
||||
showChatHistory: mockShowChatHistory
|
||||
})
|
||||
}))
|
||||
|
||||
vi.mock('@/composables/node/useNodeProgressText', () => ({
|
||||
useNodeProgressText: () => ({
|
||||
showTextPreview: vi.fn()
|
||||
})
|
||||
}))
|
||||
|
||||
// Create a local mock instead of using global to avoid conflicts
|
||||
const mockApp = {
|
||||
graph: {
|
||||
getNodeById: vi.fn()
|
||||
}
|
||||
}
|
||||
|
||||
describe('executionStore - display_component handling', () => {
|
||||
function createDisplayComponentEvent(
|
||||
nodeId: string,
|
||||
component = 'ChatHistoryWidget'
|
||||
) {
|
||||
return new CustomEvent('display_component', {
|
||||
detail: {
|
||||
node_id: nodeId,
|
||||
component,
|
||||
props: {
|
||||
history: JSON.stringify([{ prompt: 'Test', response: 'Response' }])
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function handleDisplayComponentMessage(event: CustomEvent) {
|
||||
const { node_id, component } = event.detail
|
||||
const node = mockApp.graph.getNodeById(node_id)
|
||||
if (node && component === 'ChatHistoryWidget') {
|
||||
mockShowChatHistory(node)
|
||||
}
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
setActivePinia(createPinia())
|
||||
useExecutionStore()
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
it('handles ChatHistoryWidget display_component messages', () => {
|
||||
const mockNode = { id: '123' }
|
||||
mockApp.graph.getNodeById.mockReturnValue(mockNode)
|
||||
|
||||
const event = createDisplayComponentEvent('123')
|
||||
handleDisplayComponentMessage(event)
|
||||
|
||||
expect(mockApp.graph.getNodeById).toHaveBeenCalledWith('123')
|
||||
expect(mockShowChatHistory).toHaveBeenCalledWith(mockNode)
|
||||
})
|
||||
|
||||
it('does nothing if node is not found', () => {
|
||||
mockApp.graph.getNodeById.mockReturnValue(null)
|
||||
|
||||
const event = createDisplayComponentEvent('non-existent')
|
||||
handleDisplayComponentMessage(event)
|
||||
|
||||
expect(mockApp.graph.getNodeById).toHaveBeenCalledWith('non-existent')
|
||||
expect(mockShowChatHistory).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user