mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-05-22 13:32:11 +00:00
Compare commits
10 Commits
glary/raf-
...
test/job-h
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a09613220a | ||
|
|
f826f74339 | ||
|
|
49b8576806 | ||
|
|
5b6c3a5455 | ||
|
|
91205994eb | ||
|
|
754cea7b72 | ||
|
|
725e4eabb3 | ||
|
|
7e3fb71284 | ||
|
|
58dcfbc983 | ||
|
|
1a01192140 |
@@ -24,6 +24,7 @@ import { SettingDialog } from '@e2e/fixtures/components/SettingDialog'
|
||||
import { TemplatesDialog } from '@e2e/fixtures/components/TemplatesDialog'
|
||||
import {
|
||||
AssetsSidebarTab,
|
||||
JobHistorySidebarTab,
|
||||
ModelLibrarySidebarTab,
|
||||
NodeLibrarySidebarTab,
|
||||
NodeLibrarySidebarTabV2,
|
||||
@@ -64,6 +65,7 @@ class ComfyPropertiesPanel {
|
||||
|
||||
class ComfyMenu {
|
||||
private _assetsTab: AssetsSidebarTab | null = null
|
||||
private _jobHistoryTab: JobHistorySidebarTab | null = null
|
||||
private _modelLibraryTab: ModelLibrarySidebarTab | null = null
|
||||
private _nodeLibraryTab: NodeLibrarySidebarTab | null = null
|
||||
private _nodeLibraryTabV2: NodeLibrarySidebarTabV2 | null = null
|
||||
@@ -82,6 +84,11 @@ class ComfyMenu {
|
||||
this.buttons = this.sideToolbar.locator('.side-bar-button')
|
||||
}
|
||||
|
||||
get jobHistoryTab() {
|
||||
this._jobHistoryTab ??= new JobHistorySidebarTab(this.page)
|
||||
return this._jobHistoryTab
|
||||
}
|
||||
|
||||
get modelLibraryTab() {
|
||||
this._modelLibraryTab ??= new ModelLibrarySidebarTab(this.page)
|
||||
return this._modelLibraryTab
|
||||
|
||||
@@ -30,6 +30,17 @@ class SidebarTab {
|
||||
}
|
||||
await this.tabButton.click()
|
||||
}
|
||||
|
||||
async dismissToasts() {
|
||||
const closeButtons = this.page.locator('.p-toast-close-button')
|
||||
for (const button of await closeButtons.all()) {
|
||||
await button.click().catch(() => {})
|
||||
}
|
||||
|
||||
await expect(this.page.locator('.p-toast-message'))
|
||||
.toHaveCount(0, { timeout: 5000 })
|
||||
.catch(() => {})
|
||||
}
|
||||
}
|
||||
|
||||
export class NodeLibrarySidebarTab extends SidebarTab {
|
||||
@@ -206,6 +217,64 @@ export class WorkflowsSidebarTab extends SidebarTab {
|
||||
}
|
||||
}
|
||||
|
||||
export class JobHistorySidebarTab extends SidebarTab {
|
||||
constructor(public override readonly page: Page) {
|
||||
super(page, 'job-history')
|
||||
}
|
||||
|
||||
/** Scope all locators to the sidebar root to avoid collision
|
||||
* with QueueOverlayExpanded which renders the same controls. */
|
||||
get root() {
|
||||
return this.page.locator('.sidebar-content-container')
|
||||
}
|
||||
|
||||
get allTab() {
|
||||
return this.root.getByRole('button', { name: 'All', exact: true })
|
||||
}
|
||||
|
||||
get completedTab() {
|
||||
return this.root.getByRole('button', { name: 'Completed', exact: true })
|
||||
}
|
||||
|
||||
get failedTab() {
|
||||
return this.root.getByRole('button', { name: 'Failed', exact: true })
|
||||
}
|
||||
|
||||
get searchInput() {
|
||||
return this.root.getByPlaceholder('Search...')
|
||||
}
|
||||
|
||||
get filterButton() {
|
||||
return this.root.getByRole('button', { name: /Filter/i })
|
||||
}
|
||||
|
||||
get sortButton() {
|
||||
return this.root.getByRole('button', { name: /Sort/i })
|
||||
}
|
||||
|
||||
get jobItems() {
|
||||
return this.root.locator('[data-job-id]')
|
||||
}
|
||||
|
||||
get noActiveJobsText() {
|
||||
return this.root.getByText('No active jobs')
|
||||
}
|
||||
|
||||
async waitForJobsLoad() {
|
||||
await expect(this.jobItems.first()).toBeVisible({ timeout: 5000 })
|
||||
}
|
||||
|
||||
getJobById(id: string) {
|
||||
return this.root.locator(`[data-job-id="${id}"]`)
|
||||
}
|
||||
|
||||
override async open() {
|
||||
await this.dismissToasts()
|
||||
await super.open()
|
||||
await this.allTab.waitFor({ state: 'visible', timeout: 5000 })
|
||||
}
|
||||
}
|
||||
|
||||
export class ModelLibrarySidebarTab extends SidebarTab {
|
||||
public readonly searchInput: Locator
|
||||
public readonly modelTree: Locator
|
||||
@@ -349,18 +418,6 @@ export class AssetsSidebarTab extends SidebarTab {
|
||||
await this.generatedTab.waitFor({ state: 'visible' })
|
||||
}
|
||||
|
||||
/** Dismiss all visible toast notifications by clicking their close buttons. */
|
||||
async dismissToasts() {
|
||||
const closeButtons = this.page.locator('.p-toast-close-button')
|
||||
for (const btn of await closeButtons.all()) {
|
||||
await btn.click().catch(() => {})
|
||||
}
|
||||
// Wait for all toast elements to fully animate out and detach from DOM
|
||||
await expect(this.page.locator('.p-toast-message'))
|
||||
.toHaveCount(0)
|
||||
.catch(() => {})
|
||||
}
|
||||
|
||||
async switchToImported() {
|
||||
await this.dismissToasts()
|
||||
await this.importedTab.click()
|
||||
|
||||
243
browser_tests/tests/sidebar/jobHistory.spec.ts
Normal file
243
browser_tests/tests/sidebar/jobHistory.spec.ts
Normal file
@@ -0,0 +1,243 @@
|
||||
import { expect } from '@playwright/test'
|
||||
|
||||
import { comfyPageFixture as test } from '@e2e/fixtures/ComfyPage'
|
||||
import { createMockJob } from '@e2e/fixtures/helpers/AssetsHelper'
|
||||
import type { RawJobListItem } from '@/platform/remote/comfyui/jobs/jobTypes'
|
||||
|
||||
const now = Date.now()
|
||||
|
||||
const COMPLETED_JOBS: RawJobListItem[] = [
|
||||
createMockJob({
|
||||
id: 'job-completed-1',
|
||||
status: 'completed',
|
||||
create_time: now - 60,
|
||||
execution_start_time: now - 60,
|
||||
execution_end_time: now - 50,
|
||||
outputs_count: 2
|
||||
}),
|
||||
createMockJob({
|
||||
id: 'job-completed-2',
|
||||
status: 'completed',
|
||||
create_time: now - 120,
|
||||
execution_start_time: now - 120,
|
||||
execution_end_time: now - 115,
|
||||
outputs_count: 1
|
||||
})
|
||||
]
|
||||
|
||||
const FAILED_JOBS: RawJobListItem[] = [
|
||||
createMockJob({
|
||||
id: 'job-failed-1',
|
||||
status: 'failed',
|
||||
create_time: now - 30,
|
||||
execution_start_time: now - 30,
|
||||
execution_end_time: now - 28,
|
||||
outputs_count: 0
|
||||
})
|
||||
]
|
||||
|
||||
const ALL_JOBS = [...COMPLETED_JOBS, ...FAILED_JOBS]
|
||||
|
||||
// ==========================================================================
|
||||
// 1. Tab open and job display
|
||||
// ==========================================================================
|
||||
|
||||
test.describe('Job history sidebar - display', () => {
|
||||
test.beforeEach(async ({ comfyPage }) => {
|
||||
await comfyPage.assets.mockOutputHistory(ALL_JOBS)
|
||||
await comfyPage.settings.setSetting('Comfy.Queue.QPOV2', true)
|
||||
await comfyPage.setup()
|
||||
})
|
||||
|
||||
test.afterEach(async ({ comfyPage }) => {
|
||||
await comfyPage.assets.clearMocks()
|
||||
})
|
||||
|
||||
test('Opens job history tab and shows job items', async ({ comfyPage }) => {
|
||||
const tab = comfyPage.menu.jobHistoryTab
|
||||
await tab.open()
|
||||
|
||||
await tab.waitForJobsLoad()
|
||||
await expect
|
||||
.poll(() => tab.jobItems.count(), { timeout: 5000 })
|
||||
.toBeGreaterThanOrEqual(1)
|
||||
})
|
||||
|
||||
test('Shows All, Completed filter tabs', async ({ comfyPage }) => {
|
||||
const tab = comfyPage.menu.jobHistoryTab
|
||||
await tab.open()
|
||||
|
||||
await expect(tab.allTab).toBeVisible()
|
||||
await expect(tab.completedTab).toBeVisible()
|
||||
})
|
||||
|
||||
test('Shows Failed tab when failed jobs exist', async ({ comfyPage }) => {
|
||||
const tab = comfyPage.menu.jobHistoryTab
|
||||
await tab.open()
|
||||
|
||||
await tab.waitForJobsLoad()
|
||||
await expect(tab.failedTab).toBeVisible()
|
||||
})
|
||||
|
||||
test('Shows search input and filter/sort buttons', async ({ comfyPage }) => {
|
||||
const tab = comfyPage.menu.jobHistoryTab
|
||||
await tab.open()
|
||||
|
||||
await expect(tab.searchInput).toBeVisible()
|
||||
await expect(tab.filterButton).toBeVisible()
|
||||
await expect(tab.sortButton).toBeVisible()
|
||||
})
|
||||
})
|
||||
|
||||
// ==========================================================================
|
||||
// 2. Filter tabs
|
||||
// ==========================================================================
|
||||
|
||||
test.describe('Job history sidebar - filter tabs', () => {
|
||||
test.beforeEach(async ({ comfyPage }) => {
|
||||
await comfyPage.assets.mockOutputHistory(ALL_JOBS)
|
||||
await comfyPage.settings.setSetting('Comfy.Queue.QPOV2', true)
|
||||
await comfyPage.setup()
|
||||
})
|
||||
|
||||
test.afterEach(async ({ comfyPage }) => {
|
||||
await comfyPage.assets.clearMocks()
|
||||
})
|
||||
|
||||
test('Completed tab filters to completed jobs only', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
const tab = comfyPage.menu.jobHistoryTab
|
||||
await tab.open()
|
||||
await tab.waitForJobsLoad()
|
||||
|
||||
await tab.completedTab.click()
|
||||
|
||||
await expect(tab.getJobById('job-completed-1')).toBeVisible({
|
||||
timeout: 5000
|
||||
})
|
||||
await expect(tab.getJobById('job-failed-1')).toBeHidden()
|
||||
})
|
||||
|
||||
test('Failed tab filters to failed jobs only', async ({ comfyPage }) => {
|
||||
const tab = comfyPage.menu.jobHistoryTab
|
||||
await tab.open()
|
||||
await tab.waitForJobsLoad()
|
||||
|
||||
await tab.failedTab.click()
|
||||
|
||||
await expect(tab.getJobById('job-failed-1')).toBeVisible({ timeout: 5000 })
|
||||
await expect(tab.getJobById('job-completed-1')).toBeHidden()
|
||||
})
|
||||
|
||||
test('All tab shows all jobs again', async ({ comfyPage }) => {
|
||||
const tab = comfyPage.menu.jobHistoryTab
|
||||
await tab.open()
|
||||
await tab.waitForJobsLoad()
|
||||
|
||||
// Switch to Completed then back to All
|
||||
await tab.completedTab.click()
|
||||
await expect(tab.getJobById('job-failed-1')).toBeHidden()
|
||||
|
||||
await tab.allTab.click()
|
||||
|
||||
await expect(tab.getJobById('job-completed-1')).toBeVisible({
|
||||
timeout: 5000
|
||||
})
|
||||
await expect(tab.getJobById('job-failed-1')).toBeVisible({ timeout: 5000 })
|
||||
})
|
||||
})
|
||||
|
||||
// ==========================================================================
|
||||
// 3. Search
|
||||
// ==========================================================================
|
||||
|
||||
test.describe('Job history sidebar - search', () => {
|
||||
test.beforeEach(async ({ comfyPage }) => {
|
||||
await comfyPage.assets.mockOutputHistory(ALL_JOBS)
|
||||
await comfyPage.settings.setSetting('Comfy.Queue.QPOV2', true)
|
||||
await comfyPage.setup()
|
||||
})
|
||||
|
||||
test.afterEach(async ({ comfyPage }) => {
|
||||
await comfyPage.assets.clearMocks()
|
||||
})
|
||||
|
||||
test('Search filters jobs by text', async ({ comfyPage }) => {
|
||||
const tab = comfyPage.menu.jobHistoryTab
|
||||
await tab.open()
|
||||
await tab.waitForJobsLoad()
|
||||
await expect(tab.jobItems).toHaveCount(ALL_JOBS.length, { timeout: 5000 })
|
||||
|
||||
const initialCount = await tab.jobItems.count()
|
||||
|
||||
// Search for a specific job ID substring
|
||||
await tab.searchInput.fill('failed')
|
||||
|
||||
// Wait for filter to reduce count (150ms debounce)
|
||||
await expect
|
||||
.poll(() => tab.jobItems.count(), { timeout: 5000 })
|
||||
.toBeLessThan(initialCount)
|
||||
})
|
||||
})
|
||||
|
||||
// ==========================================================================
|
||||
// 4. Empty state
|
||||
// ==========================================================================
|
||||
|
||||
test.describe('Job history sidebar - empty state', () => {
|
||||
test.beforeEach(async ({ comfyPage }) => {
|
||||
await comfyPage.assets.mockOutputHistory([])
|
||||
await comfyPage.settings.setSetting('Comfy.Queue.QPOV2', true)
|
||||
await comfyPage.setup()
|
||||
})
|
||||
|
||||
test.afterEach(async ({ comfyPage }) => {
|
||||
await comfyPage.assets.clearMocks()
|
||||
})
|
||||
|
||||
test('Shows no active jobs when history is empty', async ({ comfyPage }) => {
|
||||
const tab = comfyPage.menu.jobHistoryTab
|
||||
await tab.open()
|
||||
|
||||
await expect(tab.noActiveJobsText).toBeVisible()
|
||||
await expect(tab.jobItems).toHaveCount(0)
|
||||
})
|
||||
|
||||
test('Failed tab is hidden when no failed jobs exist', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
const tab = comfyPage.menu.jobHistoryTab
|
||||
await tab.open()
|
||||
|
||||
await expect(tab.failedTab).toBeHidden()
|
||||
})
|
||||
})
|
||||
|
||||
// ==========================================================================
|
||||
// 5. Only completed jobs (no failed tab)
|
||||
// ==========================================================================
|
||||
|
||||
test.describe('Job history sidebar - completed only', () => {
|
||||
test.beforeEach(async ({ comfyPage }) => {
|
||||
await comfyPage.assets.mockOutputHistory(COMPLETED_JOBS)
|
||||
await comfyPage.settings.setSetting('Comfy.Queue.QPOV2', true)
|
||||
await comfyPage.setup()
|
||||
})
|
||||
|
||||
test.afterEach(async ({ comfyPage }) => {
|
||||
await comfyPage.assets.clearMocks()
|
||||
})
|
||||
|
||||
test('Failed tab hidden when only completed jobs exist', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
const tab = comfyPage.menu.jobHistoryTab
|
||||
await tab.open()
|
||||
await tab.waitForJobsLoad()
|
||||
|
||||
await expect(tab.failedTab).toBeHidden()
|
||||
await expect(tab.allTab).toBeVisible()
|
||||
await expect(tab.completedTab).toBeVisible()
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user