mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-05 15:40:10 +00:00
[test] Add comprehensive unit tests for getHistory sorting
- Created tests for ComfyApi.getHistory method to verify execution_start timestamp sorting - Added tests for edge cases: missing status, empty messages, mixed message types - Verified proper error handling and parameter passing - Ensured backward compatibility with existing API interface - All 10 test cases pass successfully
This commit is contained in:
350
tests-ui/tests/scripts/api.test.ts
Normal file
350
tests-ui/tests/scripts/api.test.ts
Normal file
@@ -0,0 +1,350 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import { ComfyApi } from '@/scripts/api'
|
||||
|
||||
// Mock fetch globally
|
||||
const mockFetch = vi.fn()
|
||||
global.fetch = mockFetch
|
||||
|
||||
describe('ComfyApi', () => {
|
||||
let api: ComfyApi
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
api = new ComfyApi()
|
||||
api.api_base = '/test'
|
||||
})
|
||||
|
||||
describe('getHistory', () => {
|
||||
it('should return empty history when API call fails', async () => {
|
||||
mockFetch.mockRejectedValue(new Error('API Error'))
|
||||
|
||||
const result = await api.getHistory()
|
||||
|
||||
expect(result).toEqual({ History: [] })
|
||||
})
|
||||
|
||||
it('should return unsorted history when no execution_start messages exist', async () => {
|
||||
const mockHistoryData = {
|
||||
'1': {
|
||||
prompt: [1, 'prompt-1', {}],
|
||||
outputs: {},
|
||||
status: {
|
||||
status_str: 'success',
|
||||
completed: true,
|
||||
messages: [
|
||||
['execution_success', { prompt_id: 'prompt-1', timestamp: 1000 }]
|
||||
]
|
||||
}
|
||||
},
|
||||
'2': {
|
||||
prompt: [2, 'prompt-2', {}],
|
||||
outputs: {},
|
||||
status: {
|
||||
status_str: 'success',
|
||||
completed: true,
|
||||
messages: [
|
||||
['execution_success', { prompt_id: 'prompt-2', timestamp: 2000 }]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mockFetch.mockResolvedValue({
|
||||
json: () => Promise.resolve(mockHistoryData)
|
||||
})
|
||||
|
||||
const result = await api.getHistory()
|
||||
|
||||
expect(result.History).toHaveLength(2)
|
||||
expect(result.History[0]).toEqual({
|
||||
...mockHistoryData['1'],
|
||||
taskType: 'History'
|
||||
})
|
||||
expect(result.History[1]).toEqual({
|
||||
...mockHistoryData['2'],
|
||||
taskType: 'History'
|
||||
})
|
||||
})
|
||||
|
||||
it('should sort history by execution_start timestamp in descending order', async () => {
|
||||
const mockHistoryData = {
|
||||
'1': {
|
||||
prompt: [1, 'prompt-1', {}],
|
||||
outputs: {},
|
||||
status: {
|
||||
status_str: 'success',
|
||||
completed: true,
|
||||
messages: [
|
||||
['execution_start', { prompt_id: 'prompt-1', timestamp: 1000 }],
|
||||
['execution_success', { prompt_id: 'prompt-1', timestamp: 1100 }]
|
||||
]
|
||||
}
|
||||
},
|
||||
'2': {
|
||||
prompt: [2, 'prompt-2', {}],
|
||||
outputs: {},
|
||||
status: {
|
||||
status_str: 'success',
|
||||
completed: true,
|
||||
messages: [
|
||||
['execution_start', { prompt_id: 'prompt-2', timestamp: 3000 }],
|
||||
['execution_success', { prompt_id: 'prompt-2', timestamp: 3100 }]
|
||||
]
|
||||
}
|
||||
},
|
||||
'3': {
|
||||
prompt: [3, 'prompt-3', {}],
|
||||
outputs: {},
|
||||
status: {
|
||||
status_str: 'success',
|
||||
completed: true,
|
||||
messages: [
|
||||
['execution_start', { prompt_id: 'prompt-3', timestamp: 2000 }],
|
||||
['execution_success', { prompt_id: 'prompt-3', timestamp: 2100 }]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mockFetch.mockResolvedValue({
|
||||
json: () => Promise.resolve(mockHistoryData)
|
||||
})
|
||||
|
||||
const result = await api.getHistory()
|
||||
|
||||
expect(result.History).toHaveLength(3)
|
||||
// Should be sorted by execution_start timestamp: 3000, 2000, 1000
|
||||
expect(result.History[0].prompt[1]).toBe('prompt-2') // timestamp 3000
|
||||
expect(result.History[1].prompt[1]).toBe('prompt-3') // timestamp 2000
|
||||
expect(result.History[2].prompt[1]).toBe('prompt-1') // timestamp 1000
|
||||
})
|
||||
|
||||
it('should handle items without status or messages', async () => {
|
||||
const mockHistoryData = {
|
||||
'1': {
|
||||
prompt: [1, 'prompt-1', {}],
|
||||
outputs: {},
|
||||
status: {
|
||||
status_str: 'success',
|
||||
completed: true,
|
||||
messages: [
|
||||
['execution_start', { prompt_id: 'prompt-1', timestamp: 2000 }]
|
||||
]
|
||||
}
|
||||
},
|
||||
'2': {
|
||||
prompt: [2, 'prompt-2', {}],
|
||||
outputs: {}
|
||||
// No status field
|
||||
},
|
||||
'3': {
|
||||
prompt: [3, 'prompt-3', {}],
|
||||
outputs: {},
|
||||
status: {
|
||||
status_str: 'success',
|
||||
completed: true,
|
||||
messages: [] // Empty messages array
|
||||
}
|
||||
},
|
||||
'4': {
|
||||
prompt: [4, 'prompt-4', {}],
|
||||
outputs: {},
|
||||
status: {
|
||||
status_str: 'success',
|
||||
completed: true,
|
||||
messages: [
|
||||
['execution_start', { prompt_id: 'prompt-4', timestamp: 1000 }]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mockFetch.mockResolvedValue({
|
||||
json: () => Promise.resolve(mockHistoryData)
|
||||
})
|
||||
|
||||
const result = await api.getHistory()
|
||||
|
||||
expect(result.History).toHaveLength(4)
|
||||
// Items with execution_start should be sorted by timestamp (descending)
|
||||
// Items without execution_start should be sorted with timestamp 0 (appear last)
|
||||
expect(result.History[0].prompt[1]).toBe('prompt-1') // timestamp 2000
|
||||
expect(result.History[1].prompt[1]).toBe('prompt-4') // timestamp 1000
|
||||
// Items without execution_start timestamps should appear at the end
|
||||
expect(result.History[2].prompt[1]).toBe('prompt-2') // no status
|
||||
expect(result.History[3].prompt[1]).toBe('prompt-3') // empty messages
|
||||
})
|
||||
|
||||
it('should handle mixed message types and find execution_start', async () => {
|
||||
const mockHistoryData = {
|
||||
'1': {
|
||||
prompt: [1, 'prompt-1', {}],
|
||||
outputs: {},
|
||||
status: {
|
||||
status_str: 'success',
|
||||
completed: true,
|
||||
messages: [
|
||||
['execution_cached', { prompt_id: 'prompt-1', timestamp: 900 }],
|
||||
['execution_start', { prompt_id: 'prompt-1', timestamp: 1000 }],
|
||||
['execution_success', { prompt_id: 'prompt-1', timestamp: 1100 }]
|
||||
]
|
||||
}
|
||||
},
|
||||
'2': {
|
||||
prompt: [2, 'prompt-2', {}],
|
||||
outputs: {},
|
||||
status: {
|
||||
status_str: 'error',
|
||||
completed: true,
|
||||
messages: [
|
||||
['execution_start', { prompt_id: 'prompt-2', timestamp: 2000 }],
|
||||
[
|
||||
'execution_error',
|
||||
{
|
||||
prompt_id: 'prompt-2',
|
||||
timestamp: 2100,
|
||||
node_id: 'node-1',
|
||||
node_type: 'TestNode',
|
||||
executed: [],
|
||||
exception_message: 'Test error',
|
||||
exception_type: 'ValueError',
|
||||
traceback: [],
|
||||
current_inputs: {},
|
||||
current_outputs: {}
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mockFetch.mockResolvedValue({
|
||||
json: () => Promise.resolve(mockHistoryData)
|
||||
})
|
||||
|
||||
const result = await api.getHistory()
|
||||
|
||||
expect(result.History).toHaveLength(2)
|
||||
// Should be sorted by execution_start timestamp: 2000, 1000
|
||||
expect(result.History[0].prompt[1]).toBe('prompt-2') // timestamp 2000
|
||||
expect(result.History[1].prompt[1]).toBe('prompt-1') // timestamp 1000
|
||||
})
|
||||
|
||||
it('should respect max_items parameter', async () => {
|
||||
const mockHistoryData = {
|
||||
'1': { prompt: [1, 'prompt-1', {}], outputs: {} },
|
||||
'2': { prompt: [2, 'prompt-2', {}], outputs: {} }
|
||||
}
|
||||
|
||||
mockFetch.mockResolvedValue({
|
||||
json: () => Promise.resolve(mockHistoryData)
|
||||
})
|
||||
|
||||
await api.getHistory(50)
|
||||
|
||||
expect(mockFetch).toHaveBeenCalledWith(
|
||||
'/test/api/history?max_items=50',
|
||||
expect.objectContaining({
|
||||
cache: 'no-cache',
|
||||
headers: expect.objectContaining({
|
||||
'Comfy-User': ''
|
||||
})
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it('should use default max_items of 200', async () => {
|
||||
const mockHistoryData = {}
|
||||
|
||||
mockFetch.mockResolvedValue({
|
||||
json: () => Promise.resolve(mockHistoryData)
|
||||
})
|
||||
|
||||
await api.getHistory()
|
||||
|
||||
expect(mockFetch).toHaveBeenCalledWith(
|
||||
'/test/api/history?max_items=200',
|
||||
expect.objectContaining({
|
||||
cache: 'no-cache'
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it('should add taskType to all history items', async () => {
|
||||
const mockHistoryData = {
|
||||
'1': {
|
||||
prompt: [1, 'prompt-1', {}],
|
||||
outputs: {},
|
||||
status: {
|
||||
status_str: 'success',
|
||||
completed: true,
|
||||
messages: [
|
||||
['execution_start', { prompt_id: 'prompt-1', timestamp: 1000 }]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mockFetch.mockResolvedValue({
|
||||
json: () => Promise.resolve(mockHistoryData)
|
||||
})
|
||||
|
||||
const result = await api.getHistory()
|
||||
|
||||
expect(result.History).toHaveLength(1)
|
||||
expect(result.History[0].taskType).toBe('History')
|
||||
})
|
||||
|
||||
it('should handle empty history response', async () => {
|
||||
const mockHistoryData = {}
|
||||
|
||||
mockFetch.mockResolvedValue({
|
||||
json: () => Promise.resolve(mockHistoryData)
|
||||
})
|
||||
|
||||
const result = await api.getHistory()
|
||||
|
||||
expect(result.History).toEqual([])
|
||||
})
|
||||
|
||||
it('should handle identical timestamps consistently', async () => {
|
||||
const mockHistoryData = {
|
||||
'1': {
|
||||
prompt: [1, 'prompt-1', {}],
|
||||
outputs: {},
|
||||
status: {
|
||||
status_str: 'success',
|
||||
completed: true,
|
||||
messages: [
|
||||
['execution_start', { prompt_id: 'prompt-1', timestamp: 1000 }]
|
||||
]
|
||||
}
|
||||
},
|
||||
'2': {
|
||||
prompt: [2, 'prompt-2', {}],
|
||||
outputs: {},
|
||||
status: {
|
||||
status_str: 'success',
|
||||
completed: true,
|
||||
messages: [
|
||||
['execution_start', { prompt_id: 'prompt-2', timestamp: 1000 }]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mockFetch.mockResolvedValue({
|
||||
json: () => Promise.resolve(mockHistoryData)
|
||||
})
|
||||
|
||||
const result = await api.getHistory()
|
||||
|
||||
expect(result.History).toHaveLength(2)
|
||||
// Both items should be present, order may vary for identical timestamps
|
||||
const promptIds = result.History.map((item) => item.prompt[1])
|
||||
expect(promptIds).toContain('prompt-1')
|
||||
expect(promptIds).toContain('prompt-2')
|
||||
})
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user