mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-08 09:00:05 +00:00
test: add e2e tests for properties panel
- Add ComfyPropertiesPanel fixture class with locators and helpers - Add tests for basic functionality, search, title editing, global settings, and tab navigation - Tag all tests with @ui for filtering Amp-Thread-ID: https://ampcode.com/threads/T-019c16eb-8621-7473-9062-a57b0a1e782a Co-authored-by: Amp <amp@ampcode.com>
This commit is contained in:
@@ -13,6 +13,7 @@ import { ComfyTemplates } from '../helpers/templates'
|
||||
import { ComfyMouse } from './ComfyMouse'
|
||||
import { VueNodeHelpers } from './VueNodeHelpers'
|
||||
import { ComfyNodeSearchBox } from './components/ComfyNodeSearchBox'
|
||||
import { ComfyPropertiesPanel } from './components/ComfyPropertiesPanel'
|
||||
import { SettingDialog } from './components/SettingDialog'
|
||||
import {
|
||||
NodeLibrarySidebarTab,
|
||||
@@ -26,18 +27,6 @@ dotenv.config()
|
||||
|
||||
type WorkspaceStore = ReturnType<typeof useWorkspaceStore>
|
||||
|
||||
class ComfyPropertiesPanel {
|
||||
readonly root: Locator
|
||||
readonly panelTitle: Locator
|
||||
readonly searchBox: Locator
|
||||
|
||||
constructor(readonly page: Page) {
|
||||
this.root = page.getByTestId('properties-panel')
|
||||
this.panelTitle = this.root.locator('h3')
|
||||
this.searchBox = this.root.getByPlaceholder('Search...')
|
||||
}
|
||||
}
|
||||
|
||||
class ComfyMenu {
|
||||
private _nodeLibraryTab: NodeLibrarySidebarTab | null = null
|
||||
private _workflowsTab: WorkflowsSidebarTab | null = null
|
||||
@@ -170,6 +159,7 @@ export class ComfyPage {
|
||||
public readonly settingDialog: SettingDialog
|
||||
public readonly confirmDialog: ConfirmDialog
|
||||
public readonly vueNodes: VueNodeHelpers
|
||||
public readonly propertiesPanel: ComfyPropertiesPanel
|
||||
|
||||
/** Worker index to test user ID */
|
||||
public readonly userIds: string[] = []
|
||||
@@ -202,6 +192,7 @@ export class ComfyPage {
|
||||
this.settingDialog = new SettingDialog(page, this)
|
||||
this.confirmDialog = new ConfirmDialog(page)
|
||||
this.vueNodes = new VueNodeHelpers(page)
|
||||
this.propertiesPanel = new ComfyPropertiesPanel(page)
|
||||
}
|
||||
|
||||
convertLeafToContent(structure: FolderStructure): FolderStructure {
|
||||
|
||||
84
browser_tests/fixtures/components/ComfyPropertiesPanel.ts
Normal file
84
browser_tests/fixtures/components/ComfyPropertiesPanel.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
import type { Locator, Page } from '@playwright/test'
|
||||
|
||||
export class ComfyPropertiesPanel {
|
||||
readonly root: Locator
|
||||
readonly header: Locator
|
||||
readonly panelTitle: Locator
|
||||
readonly nodeTitleInput: Locator
|
||||
readonly closeButton: Locator
|
||||
readonly searchBox: Locator
|
||||
readonly tabList: Locator
|
||||
|
||||
constructor(readonly page: Page) {
|
||||
this.root = page.getByTestId('properties-panel')
|
||||
this.header = this.root.locator('section').first()
|
||||
this.panelTitle = this.root.locator('h3')
|
||||
this.nodeTitleInput = this.root.getByTestId('node-title-input')
|
||||
this.closeButton = this.root.getByRole('button', { name: 'Close' })
|
||||
this.searchBox = this.root.getByPlaceholder('Search...')
|
||||
this.tabList = this.root.locator('[role="tablist"]')
|
||||
}
|
||||
|
||||
getTab(tabName: string): Locator {
|
||||
return this.tabList.getByRole('tab', { name: tabName })
|
||||
}
|
||||
|
||||
async clickTab(tabName: string) {
|
||||
await this.getTab(tabName).click()
|
||||
}
|
||||
|
||||
async close() {
|
||||
await this.closeButton.click()
|
||||
}
|
||||
|
||||
async editTitle(newTitle: string) {
|
||||
await this.panelTitle.click()
|
||||
await this.nodeTitleInput.fill(newTitle)
|
||||
await this.nodeTitleInput.press('Enter')
|
||||
}
|
||||
|
||||
async cancelTitleEdit() {
|
||||
await this.panelTitle.click()
|
||||
await this.nodeTitleInput.press('Escape')
|
||||
}
|
||||
|
||||
getNodeSection(nodeTitle: string): Locator {
|
||||
return this.root.locator(`[data-testid="node-section-${nodeTitle}"]`)
|
||||
}
|
||||
|
||||
getAccordionItem(label: string): Locator {
|
||||
return this.root.locator('.border-b', { hasText: label })
|
||||
}
|
||||
|
||||
get globalSettingsSection() {
|
||||
return {
|
||||
nodes: this.getAccordionItem('Nodes'),
|
||||
canvas: this.getAccordionItem('Canvas'),
|
||||
connectionLinks: this.getAccordionItem('Connection Links'),
|
||||
viewAllSettingsButton: this.root.getByRole('button', {
|
||||
name: 'View all settings'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async toggleSwitch(switchLabel: string) {
|
||||
const switchContainer = this.root.locator('label', {
|
||||
hasText: switchLabel
|
||||
})
|
||||
const toggle = switchContainer.locator('button[role="switch"]')
|
||||
await toggle.click()
|
||||
}
|
||||
|
||||
async getSwitchState(switchLabel: string): Promise<boolean> {
|
||||
const switchContainer = this.root.locator('label', {
|
||||
hasText: switchLabel
|
||||
})
|
||||
const toggle = switchContainer.locator('button[role="switch"]')
|
||||
const ariaChecked = await toggle.getAttribute('aria-checked')
|
||||
return ariaChecked === 'true'
|
||||
}
|
||||
|
||||
get noResultsMessage(): Locator {
|
||||
return this.root.getByText('No results', { exact: false })
|
||||
}
|
||||
}
|
||||
153
browser_tests/tests/propertiesPanel/propertiesPanelBasic.spec.ts
Normal file
153
browser_tests/tests/propertiesPanel/propertiesPanelBasic.spec.ts
Normal file
@@ -0,0 +1,153 @@
|
||||
import { expect } from '@playwright/test'
|
||||
|
||||
import { comfyPageFixture as test } from '../../fixtures/ComfyPage'
|
||||
|
||||
test.describe('Properties panel basic functionality', { tag: ['@ui'] }, () => {
|
||||
test.beforeEach(async ({ comfyPage }) => {
|
||||
await comfyPage.actionbar.propertiesButton.click()
|
||||
await expect(comfyPage.propertiesPanel.root).toBeVisible()
|
||||
})
|
||||
|
||||
test.describe('Panel visibility and toggle', () => {
|
||||
test('opens panel via actionbar button', async ({ comfyPage }) => {
|
||||
const { propertiesPanel } = comfyPage
|
||||
|
||||
await propertiesPanel.close()
|
||||
await expect(propertiesPanel.root).not.toBeVisible()
|
||||
|
||||
await comfyPage.actionbar.propertiesButton.click()
|
||||
await expect(propertiesPanel.root).toBeVisible()
|
||||
})
|
||||
|
||||
test('closes panel via close button', async ({ comfyPage }) => {
|
||||
const { propertiesPanel } = comfyPage
|
||||
|
||||
await expect(propertiesPanel.root).toBeVisible()
|
||||
await propertiesPanel.closeButton.click()
|
||||
await expect(propertiesPanel.root).not.toBeVisible()
|
||||
})
|
||||
|
||||
test('persists open state after page reload', async ({ comfyPage }) => {
|
||||
const { propertiesPanel } = comfyPage
|
||||
|
||||
await expect(propertiesPanel.root).toBeVisible()
|
||||
|
||||
await comfyPage.page.reload()
|
||||
await comfyPage.setup()
|
||||
|
||||
await expect(propertiesPanel.root).toBeVisible()
|
||||
})
|
||||
})
|
||||
|
||||
test.describe('Workflow overview (no selection)', () => {
|
||||
test('shows "Workflow Overview" title when nothing selected', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
const { propertiesPanel } = comfyPage
|
||||
|
||||
await expect(propertiesPanel.panelTitle).toContainText(
|
||||
'Workflow Overview'
|
||||
)
|
||||
})
|
||||
|
||||
test('shows Parameters, Nodes, and Global Settings tabs when nothing selected', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
const { propertiesPanel } = comfyPage
|
||||
|
||||
await expect(propertiesPanel.getTab('Parameters')).toBeVisible()
|
||||
await expect(propertiesPanel.getTab('Nodes')).toBeVisible()
|
||||
await expect(propertiesPanel.getTab('Global Settings')).toBeVisible()
|
||||
})
|
||||
|
||||
test('Nodes tab displays workflow nodes', async ({ comfyPage }) => {
|
||||
const { propertiesPanel } = comfyPage
|
||||
|
||||
await propertiesPanel.clickTab('Nodes')
|
||||
await expect(propertiesPanel.root).toContainText('KSampler')
|
||||
})
|
||||
})
|
||||
|
||||
test.describe('Single node selection', () => {
|
||||
test('updates title to node name when single node selected', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
const { propertiesPanel } = comfyPage
|
||||
|
||||
await comfyPage.selectNodes(['KSampler'])
|
||||
|
||||
await expect(propertiesPanel.panelTitle).not.toContainText(
|
||||
'Workflow Overview'
|
||||
)
|
||||
})
|
||||
|
||||
test('shows Parameters, Info, and Settings tabs for single node', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
const { propertiesPanel } = comfyPage
|
||||
|
||||
await comfyPage.selectNodes(['KSampler'])
|
||||
|
||||
await expect(propertiesPanel.getTab('Parameters')).toBeVisible()
|
||||
await expect(propertiesPanel.getTab('Info')).toBeVisible()
|
||||
await expect(propertiesPanel.getTab('Settings')).toBeVisible()
|
||||
await expect(propertiesPanel.getTab('Nodes')).not.toBeVisible()
|
||||
})
|
||||
|
||||
test('Info tab shows node documentation', async ({ comfyPage }) => {
|
||||
const { propertiesPanel } = comfyPage
|
||||
|
||||
await comfyPage.selectNodes(['KSampler'])
|
||||
await propertiesPanel.clickTab('Info')
|
||||
|
||||
await expect(propertiesPanel.root).toContainText('KSampler')
|
||||
})
|
||||
|
||||
test('shows widget inputs in Parameters tab', async ({ comfyPage }) => {
|
||||
const { propertiesPanel } = comfyPage
|
||||
|
||||
await comfyPage.selectNodes(['KSampler'])
|
||||
|
||||
await expect(propertiesPanel.root.getByText('seed')).toBeVisible()
|
||||
await expect(propertiesPanel.root.getByText('steps')).toBeVisible()
|
||||
await expect(propertiesPanel.root.getByText('cfg')).toBeVisible()
|
||||
})
|
||||
})
|
||||
|
||||
test.describe('Multiple node selection', () => {
|
||||
test('shows item count in title for multiple selections', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
const { propertiesPanel } = comfyPage
|
||||
|
||||
await comfyPage.selectNodes(['KSampler', 'CLIP Text Encode (Prompt)'])
|
||||
|
||||
await expect(propertiesPanel.panelTitle).toContainText('items selected')
|
||||
})
|
||||
|
||||
test('shows Parameters and Settings tabs for multiple selection', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
const { propertiesPanel } = comfyPage
|
||||
|
||||
await comfyPage.selectNodes(['KSampler', 'CLIP Text Encode (Prompt)'])
|
||||
|
||||
await expect(propertiesPanel.getTab('Parameters')).toBeVisible()
|
||||
await expect(propertiesPanel.getTab('Settings')).toBeVisible()
|
||||
await expect(propertiesPanel.getTab('Info')).not.toBeVisible()
|
||||
})
|
||||
|
||||
test('lists all selected nodes in Parameters tab', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
const { propertiesPanel } = comfyPage
|
||||
|
||||
await comfyPage.selectNodes(['KSampler', 'CLIP Text Encode (Prompt)'])
|
||||
|
||||
await expect(propertiesPanel.root.getByText('KSampler')).toBeVisible()
|
||||
await expect(
|
||||
propertiesPanel.root.getByText('CLIP Text Encode (Prompt)')
|
||||
).toBeVisible()
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,124 @@
|
||||
import { expect } from '@playwright/test'
|
||||
|
||||
import { comfyPageFixture as test } from '../../fixtures/ComfyPage'
|
||||
|
||||
test.describe('Properties panel global settings', { tag: ['@ui'] }, () => {
|
||||
test.beforeEach(async ({ comfyPage }) => {
|
||||
await comfyPage.actionbar.propertiesButton.click()
|
||||
await expect(comfyPage.propertiesPanel.root).toBeVisible()
|
||||
await comfyPage.propertiesPanel.clickTab('Global Settings')
|
||||
})
|
||||
|
||||
test.describe('Global settings sections', () => {
|
||||
test('displays Nodes section', async ({ comfyPage }) => {
|
||||
const { propertiesPanel } = comfyPage
|
||||
|
||||
await expect(propertiesPanel.root.getByText('Nodes')).toBeVisible()
|
||||
})
|
||||
|
||||
test('displays Canvas section', async ({ comfyPage }) => {
|
||||
const { propertiesPanel } = comfyPage
|
||||
|
||||
await expect(propertiesPanel.root.getByText('Canvas')).toBeVisible()
|
||||
})
|
||||
|
||||
test('displays Connection Links section', async ({ comfyPage }) => {
|
||||
const { propertiesPanel } = comfyPage
|
||||
|
||||
await expect(
|
||||
propertiesPanel.root.getByText('Connection Links')
|
||||
).toBeVisible()
|
||||
})
|
||||
|
||||
test('has View all settings button', async ({ comfyPage }) => {
|
||||
const { propertiesPanel } = comfyPage
|
||||
|
||||
const viewAllButton = propertiesPanel.root.getByRole('button', {
|
||||
name: /View all settings/i
|
||||
})
|
||||
await expect(viewAllButton).toBeVisible()
|
||||
})
|
||||
})
|
||||
|
||||
test.describe('Nodes section settings', () => {
|
||||
test('can toggle Show advanced parameters', async ({ comfyPage }) => {
|
||||
const { propertiesPanel } = comfyPage
|
||||
|
||||
const toggle = propertiesPanel.root
|
||||
.locator('label', { hasText: 'Show advanced parameters' })
|
||||
.locator('button[role="switch"]')
|
||||
|
||||
await expect(toggle).toBeVisible()
|
||||
|
||||
const initialState = await toggle.getAttribute('aria-checked')
|
||||
await toggle.click()
|
||||
|
||||
const newState = await toggle.getAttribute('aria-checked')
|
||||
expect(newState).not.toBe(initialState)
|
||||
})
|
||||
|
||||
test('can toggle Nodes 2.0 setting', async ({ comfyPage }) => {
|
||||
const { propertiesPanel } = comfyPage
|
||||
|
||||
const toggle = propertiesPanel.root
|
||||
.locator('label', { hasText: 'Nodes 2.0' })
|
||||
.locator('button[role="switch"]')
|
||||
|
||||
await expect(toggle).toBeVisible()
|
||||
})
|
||||
})
|
||||
|
||||
test.describe('Canvas section settings', () => {
|
||||
test('can adjust grid spacing', async ({ comfyPage }) => {
|
||||
const { propertiesPanel } = comfyPage
|
||||
|
||||
const gridSpacingLabel = propertiesPanel.root.getByText('Grid Spacing')
|
||||
await expect(gridSpacingLabel).toBeVisible()
|
||||
})
|
||||
|
||||
test('can toggle Snap nodes to grid', async ({ comfyPage }) => {
|
||||
const { propertiesPanel } = comfyPage
|
||||
|
||||
const toggle = propertiesPanel.root
|
||||
.locator('label', { hasText: 'Snap nodes to grid' })
|
||||
.locator('button[role="switch"]')
|
||||
|
||||
await expect(toggle).toBeVisible()
|
||||
})
|
||||
})
|
||||
|
||||
test.describe('Connection Links section settings', () => {
|
||||
test('has link shape selector', async ({ comfyPage }) => {
|
||||
const { propertiesPanel } = comfyPage
|
||||
|
||||
const linkShapeLabel = propertiesPanel.root.getByText('Link Shape')
|
||||
await expect(linkShapeLabel).toBeVisible()
|
||||
})
|
||||
|
||||
test('can toggle Show connected links', async ({ comfyPage }) => {
|
||||
const { propertiesPanel } = comfyPage
|
||||
|
||||
const toggle = propertiesPanel.root
|
||||
.locator('label', { hasText: 'Show connected links' })
|
||||
.locator('button[role="switch"]')
|
||||
|
||||
await expect(toggle).toBeVisible()
|
||||
})
|
||||
})
|
||||
|
||||
test.describe('View all settings navigation', () => {
|
||||
test('opens full settings dialog when clicking View all settings', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
const { propertiesPanel } = comfyPage
|
||||
|
||||
const viewAllButton = propertiesPanel.root.getByRole('button', {
|
||||
name: /View all settings/i
|
||||
})
|
||||
|
||||
await viewAllButton.click()
|
||||
|
||||
await expect(comfyPage.settingDialog.root).toBeVisible()
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,73 @@
|
||||
import { expect } from '@playwright/test'
|
||||
|
||||
import { comfyPageFixture as test } from '../../fixtures/ComfyPage'
|
||||
|
||||
test.describe('Properties panel search functionality', { tag: ['@ui'] }, () => {
|
||||
test.beforeEach(async ({ comfyPage }) => {
|
||||
await comfyPage.actionbar.propertiesButton.click()
|
||||
await expect(comfyPage.propertiesPanel.root).toBeVisible()
|
||||
})
|
||||
|
||||
test.describe('Search with multiple nodes selected', () => {
|
||||
test.beforeEach(async ({ comfyPage }) => {
|
||||
await comfyPage.selectNodes(['KSampler', 'CLIP Text Encode (Prompt)'])
|
||||
})
|
||||
|
||||
test('filters widgets by search query', async ({ comfyPage }) => {
|
||||
const { propertiesPanel } = comfyPage
|
||||
|
||||
await propertiesPanel.searchBox.fill('seed')
|
||||
|
||||
await expect(propertiesPanel.root.getByText('KSampler')).toBeVisible()
|
||||
await expect(
|
||||
propertiesPanel.root.getByText('CLIP Text Encode (Prompt)')
|
||||
).not.toBeVisible()
|
||||
})
|
||||
|
||||
test('shows all nodes when search is cleared', async ({ comfyPage }) => {
|
||||
const { propertiesPanel } = comfyPage
|
||||
|
||||
await propertiesPanel.searchBox.fill('seed')
|
||||
await propertiesPanel.searchBox.fill('')
|
||||
|
||||
await expect(propertiesPanel.root.getByText('KSampler')).toBeVisible()
|
||||
await expect(
|
||||
propertiesPanel.root.getByText('CLIP Text Encode (Prompt)')
|
||||
).toBeVisible()
|
||||
})
|
||||
|
||||
test('shows no results message when search matches nothing', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
const { propertiesPanel } = comfyPage
|
||||
|
||||
await propertiesPanel.searchBox.fill('nonexistent_widget_xyz')
|
||||
|
||||
await expect(propertiesPanel.noResultsMessage).toBeVisible()
|
||||
})
|
||||
})
|
||||
|
||||
test.describe('Search with single node selected', () => {
|
||||
test('filters widgets within single node', async ({ comfyPage }) => {
|
||||
const { propertiesPanel } = comfyPage
|
||||
|
||||
await comfyPage.selectNodes(['KSampler'])
|
||||
await propertiesPanel.searchBox.fill('cfg')
|
||||
|
||||
await expect(propertiesPanel.root.getByText('cfg')).toBeVisible()
|
||||
await expect(
|
||||
propertiesPanel.root.getByText('seed', { exact: true })
|
||||
).not.toBeVisible()
|
||||
})
|
||||
})
|
||||
|
||||
test.describe('Search in workflow overview mode', () => {
|
||||
test('Nodes tab has search functionality', async ({ comfyPage }) => {
|
||||
const { propertiesPanel } = comfyPage
|
||||
|
||||
await propertiesPanel.clickTab('Nodes')
|
||||
|
||||
await expect(propertiesPanel.searchBox).toBeVisible()
|
||||
})
|
||||
})
|
||||
})
|
||||
114
browser_tests/tests/propertiesPanel/propertiesPanelTabs.spec.ts
Normal file
114
browser_tests/tests/propertiesPanel/propertiesPanelTabs.spec.ts
Normal file
@@ -0,0 +1,114 @@
|
||||
import { expect } from '@playwright/test'
|
||||
|
||||
import { comfyPageFixture as test } from '../../fixtures/ComfyPage'
|
||||
|
||||
test.describe('Properties panel tab navigation', { tag: ['@ui'] }, () => {
|
||||
test.beforeEach(async ({ comfyPage }) => {
|
||||
await comfyPage.actionbar.propertiesButton.click()
|
||||
await expect(comfyPage.propertiesPanel.root).toBeVisible()
|
||||
})
|
||||
|
||||
test.describe('Tab switching', () => {
|
||||
test('switches between tabs in workflow overview mode', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
const { propertiesPanel } = comfyPage
|
||||
|
||||
await expect(propertiesPanel.getTab('Parameters')).toBeVisible()
|
||||
|
||||
await propertiesPanel.clickTab('Nodes')
|
||||
await expect(propertiesPanel.root.getByText('KSampler')).toBeVisible()
|
||||
|
||||
await propertiesPanel.clickTab('Global Settings')
|
||||
await expect(propertiesPanel.root.getByText('Nodes')).toBeVisible()
|
||||
await expect(propertiesPanel.root.getByText('Canvas')).toBeVisible()
|
||||
})
|
||||
|
||||
test('switches between tabs in single node mode', async ({ comfyPage }) => {
|
||||
const { propertiesPanel } = comfyPage
|
||||
|
||||
await comfyPage.selectNodes(['KSampler'])
|
||||
|
||||
await expect(propertiesPanel.getTab('Parameters')).toBeVisible()
|
||||
await expect(propertiesPanel.getTab('Info')).toBeVisible()
|
||||
await expect(propertiesPanel.getTab('Settings')).toBeVisible()
|
||||
|
||||
await propertiesPanel.clickTab('Info')
|
||||
await expect(propertiesPanel.root).toContainText('KSampler')
|
||||
|
||||
await propertiesPanel.clickTab('Settings')
|
||||
await expect(
|
||||
propertiesPanel.root.getByText('Color').first()
|
||||
).toBeVisible()
|
||||
|
||||
await propertiesPanel.clickTab('Parameters')
|
||||
await expect(propertiesPanel.root.getByText('seed')).toBeVisible()
|
||||
})
|
||||
})
|
||||
|
||||
test.describe('Tab availability based on selection', () => {
|
||||
test('shows different tabs based on selection state', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
const { propertiesPanel } = comfyPage
|
||||
|
||||
await expect(propertiesPanel.getTab('Parameters')).toBeVisible()
|
||||
await expect(propertiesPanel.getTab('Nodes')).toBeVisible()
|
||||
await expect(propertiesPanel.getTab('Global Settings')).toBeVisible()
|
||||
await expect(propertiesPanel.getTab('Info')).not.toBeVisible()
|
||||
|
||||
await comfyPage.selectNodes(['KSampler'])
|
||||
await expect(propertiesPanel.getTab('Parameters')).toBeVisible()
|
||||
await expect(propertiesPanel.getTab('Info')).toBeVisible()
|
||||
await expect(propertiesPanel.getTab('Settings')).toBeVisible()
|
||||
await expect(propertiesPanel.getTab('Nodes')).not.toBeVisible()
|
||||
|
||||
await comfyPage.selectNodes(['KSampler', 'CLIP Text Encode (Prompt)'])
|
||||
await expect(propertiesPanel.getTab('Parameters')).toBeVisible()
|
||||
await expect(propertiesPanel.getTab('Settings')).toBeVisible()
|
||||
await expect(propertiesPanel.getTab('Info')).not.toBeVisible()
|
||||
await expect(propertiesPanel.getTab('Nodes')).not.toBeVisible()
|
||||
})
|
||||
|
||||
test('first tab updates for multiple selection', async ({ comfyPage }) => {
|
||||
const { propertiesPanel } = comfyPage
|
||||
|
||||
await comfyPage.selectNodes(['KSampler'])
|
||||
await expect(propertiesPanel.getTab('Parameters')).toBeVisible()
|
||||
|
||||
await comfyPage.selectNodes(['KSampler', 'CLIP Text Encode (Prompt)'])
|
||||
const firstTab = propertiesPanel.tabList.locator('[role="tab"]').first()
|
||||
await expect(firstTab).toBeVisible()
|
||||
})
|
||||
})
|
||||
|
||||
test.describe('Tab persistence', () => {
|
||||
test('remembers active tab when selection changes', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
const { propertiesPanel } = comfyPage
|
||||
|
||||
await comfyPage.selectNodes(['KSampler'])
|
||||
await propertiesPanel.clickTab('Settings')
|
||||
|
||||
await comfyPage.selectNodes(['CLIP Text Encode (Prompt)'])
|
||||
|
||||
await expect(propertiesPanel.getTab('Settings')).toBeVisible()
|
||||
})
|
||||
|
||||
test('falls back to default tab when current tab not available', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
const { propertiesPanel } = comfyPage
|
||||
|
||||
await comfyPage.selectNodes(['KSampler'])
|
||||
await propertiesPanel.clickTab('Info')
|
||||
|
||||
await comfyPage.selectNodes(['KSampler', 'CLIP Text Encode (Prompt)'])
|
||||
|
||||
await expect(propertiesPanel.getTab('Info')).not.toBeVisible()
|
||||
const firstTab = propertiesPanel.tabList.locator('[role="tab"]').first()
|
||||
await expect(firstTab).toHaveAttribute('aria-selected', 'true')
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,91 @@
|
||||
import { expect } from '@playwright/test'
|
||||
|
||||
import { comfyPageFixture as test } from '../../fixtures/ComfyPage'
|
||||
|
||||
test.describe('Properties panel title editing', { tag: ['@ui'] }, () => {
|
||||
test.beforeEach(async ({ comfyPage }) => {
|
||||
await comfyPage.actionbar.propertiesButton.click()
|
||||
await expect(comfyPage.propertiesPanel.root).toBeVisible()
|
||||
})
|
||||
|
||||
test.describe('Single node title editing', () => {
|
||||
test('shows editable title for single node selection', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
const { propertiesPanel } = comfyPage
|
||||
|
||||
await comfyPage.selectNodes(['KSampler'])
|
||||
await propertiesPanel.panelTitle.click()
|
||||
|
||||
await expect(propertiesPanel.nodeTitleInput).toBeVisible()
|
||||
})
|
||||
|
||||
test('edits node title successfully', async ({ comfyPage }) => {
|
||||
const { propertiesPanel } = comfyPage
|
||||
|
||||
await comfyPage.selectNodes(['KSampler'])
|
||||
|
||||
const newTitle = 'My Custom KSampler'
|
||||
|
||||
await propertiesPanel.panelTitle.click()
|
||||
await propertiesPanel.nodeTitleInput.fill(newTitle)
|
||||
await propertiesPanel.nodeTitleInput.press('Enter')
|
||||
|
||||
await expect(propertiesPanel.panelTitle).toContainText(newTitle)
|
||||
|
||||
const renamedNodes = await comfyPage.getNodeRefsByTitle(newTitle)
|
||||
expect(renamedNodes.length).toBeGreaterThan(0)
|
||||
})
|
||||
|
||||
test('cancels title edit with Escape', async ({ comfyPage }) => {
|
||||
const { propertiesPanel } = comfyPage
|
||||
|
||||
await comfyPage.selectNodes(['KSampler'])
|
||||
|
||||
const originalTitle = await propertiesPanel.panelTitle.innerText()
|
||||
|
||||
await propertiesPanel.panelTitle.click()
|
||||
await propertiesPanel.nodeTitleInput.fill('Should Not Be Saved')
|
||||
await propertiesPanel.nodeTitleInput.press('Escape')
|
||||
|
||||
await expect(propertiesPanel.panelTitle).toContainText(originalTitle)
|
||||
})
|
||||
|
||||
test('does not save empty title', async ({ comfyPage }) => {
|
||||
const { propertiesPanel } = comfyPage
|
||||
|
||||
await comfyPage.selectNodes(['KSampler'])
|
||||
|
||||
const originalTitle = await propertiesPanel.panelTitle.innerText()
|
||||
|
||||
await propertiesPanel.panelTitle.click()
|
||||
await propertiesPanel.nodeTitleInput.fill('')
|
||||
await propertiesPanel.nodeTitleInput.press('Enter')
|
||||
|
||||
await expect(propertiesPanel.panelTitle).toContainText(originalTitle)
|
||||
})
|
||||
})
|
||||
|
||||
test.describe('Title editing restrictions', () => {
|
||||
test('does not allow title editing for multiple selection', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
const { propertiesPanel } = comfyPage
|
||||
|
||||
await comfyPage.selectNodes(['KSampler', 'CLIP Text Encode (Prompt)'])
|
||||
await propertiesPanel.panelTitle.click()
|
||||
|
||||
await expect(propertiesPanel.nodeTitleInput).not.toBeVisible()
|
||||
})
|
||||
|
||||
test('does not allow title editing for workflow overview', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
const { propertiesPanel } = comfyPage
|
||||
|
||||
await propertiesPanel.panelTitle.click()
|
||||
|
||||
await expect(propertiesPanel.nodeTitleInput).not.toBeVisible()
|
||||
})
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user