mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-20 14:30:41 +00:00
test: add E2E Playwright tests for Node Library V2 sidebar
Add browser tests for the V2 Node Library sidebar tab gated by Comfy.NodeLibrary.NewDesign feature flag. Tests cover tab visibility, tab switching, folder expansion, and search filtering. Also adds NodeLibrarySidebarTabV2 fixture class and exposes it via ComfyPage.menu.nodeLibraryTabV2 for V2-specific locators.
This commit is contained in:
@@ -19,7 +19,9 @@ import { QueuePanel } from '@e2e/fixtures/components/QueuePanel'
|
||||
import { SettingDialog } from '@e2e/fixtures/components/SettingDialog'
|
||||
import {
|
||||
AssetsSidebarTab,
|
||||
JobHistorySidebarTab,
|
||||
NodeLibrarySidebarTab,
|
||||
NodeLibrarySidebarTabV2,
|
||||
WorkflowsSidebarTab
|
||||
} from '@e2e/fixtures/components/SidebarTab'
|
||||
import { Topbar } from '@e2e/fixtures/components/Topbar'
|
||||
@@ -55,7 +57,9 @@ class ComfyPropertiesPanel {
|
||||
|
||||
class ComfyMenu {
|
||||
private _assetsTab: AssetsSidebarTab | null = null
|
||||
private _jobHistoryTab: JobHistorySidebarTab | null = null
|
||||
private _nodeLibraryTab: NodeLibrarySidebarTab | null = null
|
||||
private _nodeLibraryTabV2: NodeLibrarySidebarTabV2 | null = null
|
||||
private _workflowsTab: WorkflowsSidebarTab | null = null
|
||||
private _topbar: Topbar | null = null
|
||||
|
||||
@@ -73,11 +77,21 @@ class ComfyMenu {
|
||||
return this.sideToolbar.locator('.side-bar-button')
|
||||
}
|
||||
|
||||
get jobHistoryTab() {
|
||||
this._jobHistoryTab ??= new JobHistorySidebarTab(this.page)
|
||||
return this._jobHistoryTab
|
||||
}
|
||||
|
||||
get nodeLibraryTab() {
|
||||
this._nodeLibraryTab ??= new NodeLibrarySidebarTab(this.page)
|
||||
return this._nodeLibraryTab
|
||||
}
|
||||
|
||||
get nodeLibraryTabV2() {
|
||||
this._nodeLibraryTabV2 ??= new NodeLibrarySidebarTabV2(this.page)
|
||||
return this._nodeLibraryTabV2
|
||||
}
|
||||
|
||||
get assetsTab() {
|
||||
this._assetsTab ??= new AssetsSidebarTab(this.page)
|
||||
return this._assetsTab
|
||||
|
||||
@@ -100,6 +100,57 @@ export class NodeLibrarySidebarTab extends SidebarTab {
|
||||
}
|
||||
}
|
||||
|
||||
export class NodeLibrarySidebarTabV2 extends SidebarTab {
|
||||
constructor(public override readonly page: Page) {
|
||||
super(page, 'node-library')
|
||||
}
|
||||
|
||||
get searchInput() {
|
||||
return this.page.getByPlaceholder('Search...')
|
||||
}
|
||||
|
||||
get sidebarContent() {
|
||||
return this.page.locator('.sidebar-content-container')
|
||||
}
|
||||
|
||||
getTab(name: string) {
|
||||
return this.sidebarContent.getByRole('tab', { name, exact: true })
|
||||
}
|
||||
|
||||
get allTab() {
|
||||
return this.getTab('All')
|
||||
}
|
||||
|
||||
get blueprintsTab() {
|
||||
return this.getTab('Blueprints')
|
||||
}
|
||||
|
||||
get sortButton() {
|
||||
return this.sidebarContent.getByRole('button', { name: 'Sort' })
|
||||
}
|
||||
|
||||
getFolder(folderName: string) {
|
||||
return this.page.locator(
|
||||
`[data-testid="node-tree-folder"][data-folder-name="${folderName}"]`
|
||||
)
|
||||
}
|
||||
|
||||
getNode(nodeName: string) {
|
||||
return this.page.locator(
|
||||
`[data-testid="node-tree-leaf"][data-node-name="${nodeName}"]`
|
||||
)
|
||||
}
|
||||
|
||||
getNodeCard(nodeName: string) {
|
||||
return this.sidebarContent.locator(`[data-node-name="${nodeName}"]`)
|
||||
}
|
||||
|
||||
override async open() {
|
||||
await super.open()
|
||||
await this.searchInput.waitFor({ state: 'visible' })
|
||||
}
|
||||
}
|
||||
|
||||
export class WorkflowsSidebarTab extends SidebarTab {
|
||||
constructor(public override readonly page: Page) {
|
||||
super(page, 'workflows')
|
||||
@@ -170,6 +221,63 @@ 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 panel to avoid collision
|
||||
* with QueueOverlayExpanded which renders the same controls. */
|
||||
private get panel() {
|
||||
return this.page.locator('.sidebar-content-container')
|
||||
}
|
||||
|
||||
get allTab() {
|
||||
return this.panel.getByRole('button', { name: 'All', exact: true })
|
||||
}
|
||||
|
||||
get completedTab() {
|
||||
return this.panel.getByRole('button', { name: 'Completed', exact: true })
|
||||
}
|
||||
|
||||
get failedTab() {
|
||||
return this.panel.getByRole('button', { name: 'Failed', exact: true })
|
||||
}
|
||||
|
||||
get searchInput() {
|
||||
return this.panel.getByPlaceholder('Search...')
|
||||
}
|
||||
|
||||
get filterButton() {
|
||||
return this.panel.getByRole('button', { name: /Filter/i })
|
||||
}
|
||||
|
||||
get sortButton() {
|
||||
return this.panel.getByRole('button', { name: /Sort/i })
|
||||
}
|
||||
|
||||
get jobItems() {
|
||||
return this.panel.locator('[data-job-id]')
|
||||
}
|
||||
|
||||
get noActiveJobsText() {
|
||||
return this.panel.getByText('No active jobs')
|
||||
}
|
||||
|
||||
getJobById(id: string) {
|
||||
return this.panel.locator(`[data-job-id="${id}"]`)
|
||||
}
|
||||
|
||||
get groupLabels() {
|
||||
return this.panel.locator('.text-xs.text-text-secondary')
|
||||
}
|
||||
|
||||
override async open() {
|
||||
await super.open()
|
||||
await this.allTab.waitFor({ state: 'visible', timeout: 5000 })
|
||||
}
|
||||
}
|
||||
|
||||
export class AssetsSidebarTab extends SidebarTab {
|
||||
constructor(public override readonly page: Page) {
|
||||
super(page, 'assets')
|
||||
|
||||
70
browser_tests/tests/sidebar/nodeLibraryV2.spec.ts
Normal file
70
browser_tests/tests/sidebar/nodeLibraryV2.spec.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
import { expect } from '@playwright/test'
|
||||
|
||||
import { comfyPageFixture as test } from '../../fixtures/ComfyPage'
|
||||
|
||||
test.describe('Node library sidebar V2', () => {
|
||||
test.beforeEach(async ({ comfyPage }) => {
|
||||
await comfyPage.settings.setSetting('Comfy.UseNewMenu', 'Top')
|
||||
await comfyPage.settings.setSetting('Comfy.NodeLibrary.NewDesign', true)
|
||||
|
||||
const tab = comfyPage.menu.nodeLibraryTabV2
|
||||
await tab.open()
|
||||
})
|
||||
|
||||
test('Shows search input and tab buttons', async ({ comfyPage }) => {
|
||||
const tab = comfyPage.menu.nodeLibraryTabV2
|
||||
|
||||
await expect(tab.searchInput).toBeVisible()
|
||||
await expect(tab.allTab).toBeVisible()
|
||||
await expect(tab.blueprintsTab).toBeVisible()
|
||||
})
|
||||
|
||||
test('All tab is selected by default', async ({ comfyPage }) => {
|
||||
const tab = comfyPage.menu.nodeLibraryTabV2
|
||||
|
||||
await expect(tab.allTab).toHaveAttribute('aria-selected', 'true')
|
||||
})
|
||||
|
||||
test('Can switch between tabs', async ({ comfyPage }) => {
|
||||
const tab = comfyPage.menu.nodeLibraryTabV2
|
||||
|
||||
await tab.blueprintsTab.click()
|
||||
await expect(tab.blueprintsTab).toHaveAttribute('aria-selected', 'true')
|
||||
await expect(tab.allTab).toHaveAttribute('aria-selected', 'false')
|
||||
|
||||
await tab.allTab.click()
|
||||
await expect(tab.allTab).toHaveAttribute('aria-selected', 'true')
|
||||
await expect(tab.blueprintsTab).toHaveAttribute('aria-selected', 'false')
|
||||
})
|
||||
|
||||
test('All tab displays node tree with folders', async ({ comfyPage }) => {
|
||||
const tab = comfyPage.menu.nodeLibraryTabV2
|
||||
|
||||
await expect(tab.allTab).toHaveAttribute('aria-selected', 'true')
|
||||
await expect(tab.getFolder('sampling').first()).toBeVisible()
|
||||
})
|
||||
|
||||
test('Can expand folder and see nodes in All tab', async ({ comfyPage }) => {
|
||||
const tab = comfyPage.menu.nodeLibraryTabV2
|
||||
|
||||
await tab.getFolder('sampling').first().click()
|
||||
await expect(tab.getNode('KSampler (Advanced)').first()).toBeVisible()
|
||||
})
|
||||
|
||||
test('Search filters nodes in All tab', async ({ comfyPage }) => {
|
||||
const tab = comfyPage.menu.nodeLibraryTabV2
|
||||
|
||||
await tab.searchInput.click()
|
||||
await tab.searchInput.fill('KSampler')
|
||||
// Wait for debounced search to take effect
|
||||
await expect(tab.getNode('KSampler (Advanced)').first()).toBeVisible({
|
||||
timeout: 5000
|
||||
})
|
||||
})
|
||||
|
||||
test('Sort button is visible', async ({ comfyPage }) => {
|
||||
const tab = comfyPage.menu.nodeLibraryTabV2
|
||||
|
||||
await expect(tab.sortButton).toBeVisible()
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user