add tests - fix history_v2/:prompt_id fmt

This commit is contained in:
Richard Yu
2025-07-12 14:27:02 -07:00
parent 6dc75e2dcc
commit 75faab2a28
6 changed files with 215 additions and 12 deletions

View File

@@ -73,7 +73,7 @@ export default class TaskHistory {
return route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify(this.tasks)
body: JSON.stringify({ history: this.tasks })
})
}
@@ -95,7 +95,7 @@ export default class TaskHistory {
async setupRoutes() {
return this.comfyPage.page.route(
/.*\/api\/(view|history)(\?.*)?$/,
/.*\/api\/(view|history|history_v2)(\?.*)?$/,
async (route) => {
const request = route.request()
const method = request.method()

View File

@@ -187,12 +187,14 @@ test.describe('Workflows sidebar', () => {
test('Can save workflow as with same name', async ({ comfyPage }) => {
await comfyPage.menu.topbar.saveWorkflow('workflow5.json')
await comfyPage.nextFrame()
expect(await comfyPage.menu.workflowsTab.getOpenedWorkflowNames()).toEqual([
'workflow5.json'
])
await comfyPage.menu.topbar.saveWorkflowAs('workflow5.json')
await comfyPage.confirmDialog.click('overwrite')
await comfyPage.nextFrame()
expect(await comfyPage.menu.workflowsTab.getOpenedWorkflowNames()).toEqual([
'workflow5.json'
])

View File

@@ -214,12 +214,11 @@ const menuItems = computed<MenuItem[]>(() => {
void workflowService.loadTaskWorkflow(menuTargetTask.value)
}
},
disabled:
!menuTargetTask.value?.workflow &&
!(
menuTargetTask.value?.isHistory &&
menuTargetTask.value?.prompt.prompt_id
)
disabled: !(
menuTargetTask.value?.workflow ||
(menuTargetTask.value?.isHistory &&
menuTargetTask.value?.prompt.prompt_id)
)
},
{
label: t('g.goToNode'),

View File

@@ -743,13 +743,13 @@ export class ComfyApi extends EventTarget {
const json = await res.json()
// The /history_v2/{prompt_id} endpoint returns data for a specific prompt
// The response format is: { prompt_id: { prompt: [...], outputs: {...}, status: {...} } }
// The response format is: { prompt_id: { prompt: {priority, prompt_id, extra_data}, outputs: {...}, status: {...} } }
const historyItem = json[prompt_id]
if (!historyItem) return null
// Extract workflow from the prompt array
// prompt[3] contains extra_data which has extra_pnginfo.workflow
const workflow = historyItem.prompt?.[3]?.extra_pnginfo?.workflow
// Extract workflow from the prompt object
// prompt.extra_data contains extra_pnginfo.workflow
const workflow = historyItem.prompt?.extra_data?.extra_pnginfo?.workflow
return workflow || null
} catch (error) {
console.error(`Failed to fetch workflow for prompt ${prompt_id}:`, error)

View File

@@ -4,6 +4,7 @@ import type {
HistoryResponse,
RawHistoryItem
} from '../../../src/schemas/apiSchema'
import type { ComfyWorkflowJSON } from '../../../src/schemas/comfyWorkflowSchema'
import { ComfyApi } from '../../../src/scripts/api'
describe('ComfyApi getHistory', () => {
@@ -124,3 +125,124 @@ describe('ComfyApi getHistory', () => {
})
})
})
describe('ComfyApi getWorkflowFromHistory', () => {
let api: ComfyApi
beforeEach(() => {
api = new ComfyApi()
})
const mockWorkflow: ComfyWorkflowJSON = {
last_node_id: 1,
last_link_id: 0,
nodes: [],
links: [],
groups: [],
config: {},
extra: {},
version: 0.4
}
it('should fetch workflow data for a specific prompt', async () => {
const promptId = 'test_prompt_id'
const mockResponse = {
[promptId]: {
prompt: {
priority: 0,
prompt_id: promptId,
extra_data: {
extra_pnginfo: {
workflow: mockWorkflow
}
}
},
outputs: {},
status: {
status_str: 'success',
completed: true,
messages: []
}
}
}
const mockFetchApi = vi.fn().mockResolvedValue({
json: vi.fn().mockResolvedValue(mockResponse)
})
api.fetchApi = mockFetchApi
const result = await api.getWorkflowFromHistory(promptId)
expect(mockFetchApi).toHaveBeenCalledWith(`/history_v2/${promptId}`)
expect(result).toEqual(mockWorkflow)
})
it('should return null when prompt_id is not found', async () => {
const promptId = 'non_existent_prompt'
const mockResponse = {}
const mockFetchApi = vi.fn().mockResolvedValue({
json: vi.fn().mockResolvedValue(mockResponse)
})
api.fetchApi = mockFetchApi
const result = await api.getWorkflowFromHistory(promptId)
expect(mockFetchApi).toHaveBeenCalledWith(`/history_v2/${promptId}`)
expect(result).toBeNull()
})
it('should return null when workflow data is missing', async () => {
const promptId = 'test_prompt_id'
const mockResponse = {
[promptId]: {
prompt: {
priority: 0,
prompt_id: promptId,
extra_data: {}
},
outputs: {},
status: {
status_str: 'success',
completed: true,
messages: []
}
}
}
const mockFetchApi = vi.fn().mockResolvedValue({
json: vi.fn().mockResolvedValue(mockResponse)
})
api.fetchApi = mockFetchApi
const result = await api.getWorkflowFromHistory(promptId)
expect(result).toBeNull()
})
it('should handle API errors gracefully', async () => {
const promptId = 'test_prompt_id'
const mockFetchApi = vi.fn().mockRejectedValue(new Error('Network error'))
api.fetchApi = mockFetchApi
const result = await api.getWorkflowFromHistory(promptId)
expect(result).toBeNull()
})
it('should handle malformed response gracefully', async () => {
const promptId = 'test_prompt_id'
const mockResponse = {
[promptId]: null
}
const mockFetchApi = vi.fn().mockResolvedValue({
json: vi.fn().mockResolvedValue(mockResponse)
})
api.fetchApi = mockFetchApi
const result = await api.getWorkflowFromHistory(promptId)
expect(result).toBeNull()
})
})

View File

@@ -3,6 +3,86 @@ import { describe, expect, it } from 'vitest'
import { TaskItemImpl } from '@/stores/queueStore'
describe('TaskItemImpl', () => {
describe('prompt property accessors', () => {
it('should correctly access queueIndex from priority', () => {
const taskItem = new TaskItemImpl('Pending', {
priority: 5,
prompt_id: 'test-id',
extra_data: { client_id: 'client-id' }
})
expect(taskItem.queueIndex).toBe(5)
})
it('should correctly access promptId from prompt_id', () => {
const taskItem = new TaskItemImpl('History', {
priority: 0,
prompt_id: 'unique-prompt-id',
extra_data: { client_id: 'client-id' }
})
expect(taskItem.promptId).toBe('unique-prompt-id')
})
it('should correctly access extraData', () => {
const extraData = {
client_id: 'client-id',
extra_pnginfo: {
workflow: {
last_node_id: 1,
last_link_id: 0,
nodes: [],
links: [],
groups: [],
config: {},
extra: {},
version: 0.4
}
}
}
const taskItem = new TaskItemImpl('Running', {
priority: 1,
prompt_id: 'test-id',
extra_data: extraData
})
expect(taskItem.extraData).toEqual(extraData)
})
it('should correctly access workflow from extraPngInfo', () => {
const workflow = {
last_node_id: 1,
last_link_id: 0,
nodes: [],
links: [],
groups: [],
config: {},
extra: {},
version: 0.4
}
const taskItem = new TaskItemImpl('History', {
priority: 0,
prompt_id: 'test-id',
extra_data: {
client_id: 'client-id',
extra_pnginfo: { workflow }
}
})
expect(taskItem.workflow).toEqual(workflow)
})
it('should return undefined workflow when extraPngInfo is missing', () => {
const taskItem = new TaskItemImpl('History', {
priority: 0,
prompt_id: 'test-id',
extra_data: { client_id: 'client-id' }
})
expect(taskItem.workflow).toBeUndefined()
})
})
it('should remove animated property from outputs during construction', () => {
const taskItem = new TaskItemImpl(
'History',