Compare commits

...

3 Commits

Author SHA1 Message Date
Johnpaul
1034b5030f fix: restore toBeVisible assertion in enterBuilder after merge 2026-04-09 00:41:48 +01:00
Johnpaul Chiwetelu
d5622eef75 Merge branch 'main' into test/add-visibility-assertions-before-click 2026-04-09 00:40:59 +01:00
Johnpaul
fb95fddf0d test: add toBeVisible assertions before every click in E2E tests
Adds `await expect(locator).toBeVisible()` before `.click()` calls
across 104 test and fixture files (431 assertions). Gives immediate,
descriptive failures instead of generic actionability timeouts.

Skips force clicks, canvas/mouse coordinate clicks, custom click
methods, catch chains, and toPass retry blocks. Updates
FLAKE_PREVENTION_RULES.md with the new rule.
2026-04-09 00:27:01 +01:00
105 changed files with 544 additions and 6 deletions

View File

@@ -11,6 +11,7 @@ exist to make flaky-test triage faster and more repeatable.
Before merging a flaky-test fix, confirm all of these are true:
- the latest CI artifact was inspected directly
- every `.click()` is preceded by `await expect(locator).toBeVisible()`
- the root cause is stated as a race or readiness mismatch
- the fix waits on the real readiness boundary
- the assertion primitive matches the job
@@ -26,7 +27,25 @@ Before merging a flaky-test fix, confirm all of these are true:
- Pull the newest run after each push instead of assuming the flaky set is
unchanged.
## 2. Wait For The Real Readiness Boundary
## 2. Assert Visibility Before Every Click
- Every `await locator.click()` must be preceded by
`await expect(locator).toBeVisible()`.
- This gives an immediate, descriptive failure ("expected element to be visible")
instead of a generic actionability timeout.
- Exceptions (do NOT add the assertion):
- `click({ force: true })` — intentionally bypasses actionability checks.
- Canvas / mouse coordinate clicks (`comfyPage.canvas.click(...)`,
`page.mouse.click(x, y)`).
- Custom click methods with string args (`node.click('title')`,
`confirmDialog.click('save')`).
- Clicks inside `.catch()` chains (fire-and-forget cleanup).
- Clicks inside `expect(async () => { ... }).toPass()` retry blocks.
- `NodeWidgetReference.click()` (canvas coordinate-based).
- If a visibility check already exists within the preceding 3 lines
(`toBeVisible()`, `waitFor({ state: 'visible' })`), do not duplicate it.
## 3. Wait For The Real Readiness Boundary
- Visible is not always ready.
- If the behavior depends on internal state, wait on that state.
@@ -42,7 +61,7 @@ Common readiness boundaries:
- locale-triggered workflow reload finished before selecting nodes
- real builder UI ready, not transient helper metadata
## 3. Choose The Smallest Correct Assertion
## 4. Choose The Smallest Correct Assertion
- Use built-in retrying locator assertions when locator state is the behavior.
- Use `expect.poll()` for a single async value.
@@ -58,7 +77,7 @@ await expect
.toEqual([])
```
## 4. Prefer Behavioral Assertions
## 5. Prefer Behavioral Assertions
- Use screenshots only when appearance is the behavior under test.
- If a screenshot only indirectly proves behavior, replace it with a direct
@@ -66,7 +85,7 @@ await expect
- Prefer assertions on link counts, positions, visible menu items, persisted
settings, and node state.
## 5. Keep Helper Changes Narrow
## 6. Keep Helper Changes Narrow
- Shared helpers should drive setup to a stable boundary.
- Do not encode one-spec timing assumptions into generic helpers.
@@ -74,7 +93,7 @@ await expect
- If a helper fails before the real test begins, remove or relax the brittle
precondition and let downstream UI interaction prove readiness.
## 6. Verify Narrowly
## 7. Verify Narrowly
- Prefer targeted reruns through `pnpm test:browser:local`.
- On Windows, prefer `file:line` or whole-spec arguments over `--grep` when the

View File

@@ -1,5 +1,5 @@
import type { APIRequestContext, Locator, Page } from '@playwright/test'
import { test as base } from '@playwright/test'
import { expect, test as base } from '@playwright/test'
import { config as dotenvConfig } from 'dotenv'
import { NodeBadgeMode } from '@/types/nodeSource'
@@ -116,6 +116,7 @@ class ComfyMenu {
async toggleTheme() {
const currentTheme = await this.getThemeId()
await expect(this.modeToggleButton).toBeVisible()
await this.modeToggleButton.click()
await this.page.waitForFunction(
(prevTheme) => {

View File

@@ -2,6 +2,7 @@
* Vue Node Test Helpers
*/
import type { Locator, Page } from '@playwright/test'
import { expect } from '@playwright/test'
import { TestIds } from '@e2e/fixtures/selectors'
import { VueNodeFixture } from '@e2e/fixtures/utils/vueNodeFixtures'
@@ -210,6 +211,7 @@ export class VueNodeHelpers {
'subgraph-enter-button has no bounding box: element may be hidden or not in DOM'
)
}
await expect(editButton).toBeVisible()
await editButton.click({
position: { x: box.width / 2, y: box.height * 0.75 }
})

View File

@@ -23,6 +23,9 @@ export class ComfyNodeSearchFilterSelectionPanel {
}
async selectFilterValue(filterValue: string) {
await expect(
this.page.locator('.filter-value-select .p-select-dropdown')
).toBeVisible()
await this.page.locator('.filter-value-select .p-select-dropdown').click()
await this.page
.locator(
@@ -34,6 +37,9 @@ export class ComfyNodeSearchFilterSelectionPanel {
async addFilter(filterValue: string, filterType: string) {
await this.selectFilterType(filterType)
await this.selectFilterValue(filterValue)
await expect(
this.page.getByRole('button', { name: 'Add', exact: true })
).toBeVisible()
await this.page.getByRole('button', { name: 'Add', exact: true }).click()
}
}
@@ -74,6 +80,7 @@ export class ComfyNodeSearchBox {
}
async addFilter(filterValue: string, filterType: string) {
await expect(this.filterButton).toBeVisible()
await this.filterButton.click()
await this.filterSelectionPanel.addFilter(filterValue, filterType)
}
@@ -85,6 +92,9 @@ export class ComfyNodeSearchBox {
}
async removeFilter(index: number) {
await expect(
this.filterChips.nth(index).locator('.p-chip-remove-icon')
).toBeVisible()
await this.filterChips.nth(index).locator('.p-chip-remove-icon').click()
}

View File

@@ -17,14 +17,21 @@ export class ContextMenu {
}
async clickMenuItem(name: string): Promise<void> {
await expect(this.page.getByRole('menuitem', { name })).toBeVisible()
await this.page.getByRole('menuitem', { name }).click()
}
async clickMenuItemExact(name: string): Promise<void> {
await expect(
this.page.getByRole('menuitem', { name, exact: true })
).toBeVisible()
await this.page.getByRole('menuitem', { name, exact: true }).click()
}
async clickLitegraphMenuItem(name: string): Promise<void> {
await expect(
this.page.locator(`.litemenu-entry:has-text("${name}")`)
).toBeVisible()
await this.page.locator(`.litemenu-entry:has-text("${name}")`).click()
}
@@ -47,6 +54,7 @@ export class ContextMenu {
}
async openFor(locator: Locator): Promise<this> {
await expect(locator).toBeVisible()
await locator.click({ button: 'right' })
await expect.poll(() => this.isVisible()).toBe(true)
return this
@@ -58,6 +66,7 @@ export class ContextMenu {
* right-click so the correct per-node menu items appear.
*/
async openForVueNode(header: Locator): Promise<this> {
await expect(header).toBeVisible()
await header.click()
await header.click({ button: 'right' })
await this.primeVueMenu.waitFor({ state: 'visible' })

View File

@@ -13,6 +13,7 @@ export class QueuePanel {
}
async openClearHistoryDialog() {
await expect(this.moreOptionsButton).toBeVisible()
await this.moreOptionsButton.click()
const clearHistoryAction = this.page.getByTestId(

View File

@@ -1,4 +1,5 @@
import type { Page } from '@playwright/test'
import { expect } from '@playwright/test'
import type { ComfyPage } from '@e2e/fixtures/ComfyPage'
import { TestIds } from '@e2e/fixtures/selectors'
@@ -33,6 +34,7 @@ export class SettingDialog extends BaseDialog {
*/
async toggleBooleanSetting(id: string) {
const settingInputDiv = this.root.locator(`div[id="${id}"]`)
await expect(settingInputDiv.locator('input')).toBeVisible()
await settingInputDiv.locator('input').click()
}
@@ -56,6 +58,7 @@ export class SettingDialog extends BaseDialog {
const aboutButton = this.root.locator('nav').getByRole('button', {
name: 'About'
})
await expect(aboutButton).toBeVisible()
await aboutButton.click()
await this.page.waitForSelector('.about-container')
}

View File

@@ -24,12 +24,14 @@ class SidebarTab {
if (await this.selectedTabButton.isVisible()) {
return
}
await expect(this.tabButton).toBeVisible()
await this.tabButton.click()
}
async close() {
if (!this.tabButton.isVisible()) {
return
}
await expect(this.tabButton).toBeVisible()
await this.tabButton.click()
}
}
@@ -69,6 +71,7 @@ export class NodeLibrarySidebarTab extends SidebarTab {
return
}
await expect(this.tabButton).toBeVisible()
await this.tabButton.click()
await this.nodeLibraryTree.waitFor({ state: 'hidden' })
}
@@ -143,6 +146,7 @@ export class NodeLibrarySidebarTabV2 extends SidebarTab {
const folder = this.getFolder(folderName)
const isExpanded = await folder.getAttribute('aria-expanded')
if (isExpanded !== 'true') {
await expect(folder).toBeVisible()
await folder.click()
}
}
@@ -182,6 +186,7 @@ export class WorkflowsSidebarTab extends SidebarTab {
async switchToWorkflow(workflowName: string) {
const workflowLocator = this.getOpenedItem(workflowName)
await expect(workflowLocator).toBeVisible()
await workflowLocator.click()
}
@@ -198,6 +203,7 @@ export class WorkflowsSidebarTab extends SidebarTab {
}
async renameWorkflow(locator: Locator, newName: string) {
await expect(locator).toBeVisible()
await locator.click({ button: 'right' })
await this.page
.locator('.p-contextmenu-item-content', { hasText: 'Rename' })
@@ -216,6 +222,7 @@ export class WorkflowsSidebarTab extends SidebarTab {
}
async insertWorkflow(locator: Locator) {
await expect(locator).toBeVisible()
await locator.click({ button: 'right' })
await this.page
.locator('.p-contextmenu-item-content', { hasText: 'Insert' })
@@ -429,6 +436,7 @@ export class AssetsSidebarTab extends SidebarTab {
async switchToImported() {
await this.dismissToasts()
await expect(this.importedTab).toBeVisible()
await this.importedTab.click()
await expect(this.importedTab).toHaveAttribute('aria-selected', 'true', {
timeout: 3000
@@ -437,6 +445,7 @@ export class AssetsSidebarTab extends SidebarTab {
async switchToGenerated() {
await this.dismissToasts()
await expect(this.generatedTab).toBeVisible()
await this.generatedTab.click()
await expect(this.generatedTab).toHaveAttribute('aria-selected', 'true', {
timeout: 3000
@@ -445,6 +454,7 @@ export class AssetsSidebarTab extends SidebarTab {
async openSettingsMenu() {
await this.dismissToasts()
await expect(this.settingsButton).toBeVisible()
await this.settingsButton.click()
// Wait for popover content to render
await this.listViewOption
@@ -455,6 +465,7 @@ export class AssetsSidebarTab extends SidebarTab {
async rightClickAsset(name: string) {
const card = this.getAssetCardByName(name)
await expect(card).toBeVisible()
await card.click({ button: 'right' })
await this.page
.locator('.p-contextmenu')

View File

@@ -1,4 +1,5 @@
import type { Locator, Page } from '@playwright/test'
import { expect } from '@playwright/test'
import type { WorkspaceStore } from '@e2e/types/globals'
@@ -128,6 +129,7 @@ export class Topbar {
await this.menuLocator.waitFor({ state: 'hidden', timeout: 1000 })
}
await expect(this.menuTrigger).toBeVisible()
await this.menuTrigger.click()
await this.menuLocator.waitFor({ state: 'visible' })
return this.menuLocator
@@ -171,6 +173,7 @@ export class Topbar {
const { darkTheme, lightTheme } = await this.getThemeMenuItems()
const themeItem = theme === 'dark' ? darkTheme : lightTheme
const themeLabel = themeItem.locator('.p-menubar-item-label')
await expect(themeLabel).toBeVisible()
await themeLabel.click()
}
@@ -189,6 +192,7 @@ export class Topbar {
// Handle top-level commands (like "New")
if (path.length === 1) {
await expect(topLevelMenuItem).toBeVisible()
await topLevelMenuItem.click()
return
}
@@ -203,6 +207,7 @@ export class Topbar {
// Click outside to reset, then reopen menu
await this.page.locator('body').click({ position: { x: 500, y: 300 } })
await this.menuLocator.waitFor({ state: 'hidden', timeout: 1000 })
await expect(this.menuTrigger).toBeVisible()
await this.menuTrigger.click()
await this.menuLocator.waitFor({ state: 'visible' })
// Re-hover on top-level menu to trigger submenu
@@ -228,6 +233,7 @@ export class Topbar {
await menuItem.hover()
currentMenu = menuItem
}
await expect(currentMenu).toBeVisible()
await currentMenu.click()
}
}

View File

@@ -1,4 +1,5 @@
import type { Locator, Page } from '@playwright/test'
import { expect } from '@playwright/test'
import type { ComfyPage } from '@e2e/fixtures/ComfyPage'
import { TestIds } from '@e2e/fixtures/selectors'
@@ -45,6 +46,9 @@ export class AppModeHelper {
.getByRole('button', { name: 'Workflow actions' })
.first()
.click()
await expect(
this.page.getByRole('menuitem', { name: /Build app|Edit app/ })
).toBeVisible()
await this.page
.getByRole('menuitem', { name: /Build app|Edit app/ })
.click()

View File

@@ -1,4 +1,5 @@
import type { Locator, Page } from '@playwright/test'
import { expect } from '@playwright/test'
import type { ComfyPage } from '@e2e/fixtures/ComfyPage'
@@ -52,6 +53,7 @@ export class AppModeWidgetHelper {
/** Select an option from a combo/select widget. */
async selectOption(key: string, optionName: string) {
const widget = this.getWidgetItem(key)
await expect(widget.getByRole('combobox')).toBeVisible()
await widget.getByRole('combobox').click()
await this.page
.getByRole('option', { name: optionName, exact: true })
@@ -84,6 +86,7 @@ export class AppModeWidgetHelper {
)
const responsePromise = this.page.waitForResponse('**/api/prompt')
await expect(this.comfyPage.appMode.runButton).toBeVisible()
await this.comfyPage.appMode.runButton.click()
await responsePromise

View File

@@ -1,4 +1,5 @@
import type { Locator, Page } from '@playwright/test'
import { expect } from '@playwright/test'
import type { ComfyPage } from '@e2e/fixtures/ComfyPage'
import { TestIds } from '@e2e/fixtures/selectors'
@@ -51,22 +52,29 @@ export class BuilderFooterHelper {
}
async next() {
await expect(this.nextButton).toBeVisible()
await this.nextButton.click()
await this.comfyPage.nextFrame()
}
async back() {
await expect(this.backButton).toBeVisible()
await this.backButton.click()
await this.comfyPage.nextFrame()
}
async exitBuilder() {
await expect(this.exitButton).toBeVisible()
await this.exitButton.click()
await this.comfyPage.nextFrame()
}
async openSaveAsFromChevron() {
await expect(this.saveAsChevron).toBeVisible()
await this.saveAsChevron.click()
await expect(
this.page.getByRole('menuitem', { name: 'Save as' })
).toBeVisible()
await this.page.getByRole('menuitem', { name: 'Save as' }).click()
await this.comfyPage.nextFrame()
}

View File

@@ -1,4 +1,5 @@
import type { Locator, Page } from '@playwright/test'
import { expect } from '@playwright/test'
import type { ComfyPage } from '@e2e/fixtures/ComfyPage'
@@ -72,7 +73,9 @@ export class BuilderSaveAsHelper {
async fillAndSave(workflowName: string, viewType: 'App' | 'Node graph') {
await this.nameInput.fill(workflowName)
await expect(this.viewTypeRadio(viewType)).toBeVisible()
await this.viewTypeRadio(viewType).click()
await expect(this.saveButton).toBeVisible()
await this.saveButton.click()
}
}

View File

@@ -1,4 +1,5 @@
import type { Locator, Page } from '@playwright/test'
import { expect } from '@playwright/test'
import type { ComfyPage } from '@e2e/fixtures/ComfyPage'
import { TestIds } from '@e2e/fixtures/selectors'
@@ -66,7 +67,9 @@ export class BuilderSelectHelper {
/** Delete a builder input via its actions menu. */
async deleteInput(title: string) {
const menu = this.getInputItemMenu(title)
await expect(menu).toBeVisible()
await menu.click()
await expect(this.page.getByText('Delete', { exact: true })).toBeVisible()
await this.page.getByText('Delete', { exact: true }).click()
await this.comfyPage.nextFrame()
}
@@ -78,7 +81,9 @@ export class BuilderSelectHelper {
*/
async renameInputViaMenu(title: string, newName: string) {
const menu = this.getInputItemMenu(title)
await expect(menu).toBeVisible()
await menu.click()
await expect(this.page.getByText('Rename', { exact: true })).toBeVisible()
await this.page.getByText('Rename', { exact: true }).click()
const input = this.page
@@ -114,7 +119,9 @@ export class BuilderSelectHelper {
* @param newName The new name to assign.
*/
async renameWidget(popoverTrigger: Locator, newName: string) {
await expect(popoverTrigger).toBeVisible()
await popoverTrigger.click()
await expect(this.page.getByText('Rename', { exact: true })).toBeVisible()
await this.page.getByText('Rename', { exact: true }).click()
const dialogInput = this.page.locator(

View File

@@ -1,4 +1,5 @@
import type { Locator, Page } from '@playwright/test'
import { expect } from '@playwright/test'
import type { ComfyPage } from '@e2e/fixtures/ComfyPage'
@@ -14,16 +15,25 @@ export class BuilderStepsHelper {
}
async goToInputs() {
await expect(
this.toolbar.getByRole('button', { name: 'Inputs' })
).toBeVisible()
await this.toolbar.getByRole('button', { name: 'Inputs' }).click()
await this.comfyPage.nextFrame()
}
async goToOutputs() {
await expect(
this.toolbar.getByRole('button', { name: 'Outputs' })
).toBeVisible()
await this.toolbar.getByRole('button', { name: 'Outputs' }).click()
await this.comfyPage.nextFrame()
}
async goToPreview() {
await expect(
this.toolbar.getByRole('button', { name: 'Preview' })
).toBeVisible()
await this.toolbar.getByRole('button', { name: 'Preview' }).click()
await this.comfyPage.nextFrame()
}

View File

@@ -1,4 +1,5 @@
import type { Locator, Page } from '@playwright/test'
import { expect } from '@playwright/test'
import { DefaultGraphPositions } from '@e2e/fixtures/constants/defaultGraphPositions'
import type { Position } from '@e2e/fixtures/types'
@@ -18,6 +19,7 @@ export class CanvasHelper {
async resetView(): Promise<void> {
if (await this.resetViewButton.isVisible()) {
await expect(this.resetViewButton).toBeVisible()
await this.resetViewButton.click()
}
await this.page.mouse.move(10, 10)

View File

@@ -1,4 +1,5 @@
import type { Locator } from '@playwright/test'
import { expect } from '@playwright/test'
import type { LGraph, LGraphNode } from '@/lib/litegraph/src/litegraph'
import type { NodeId } from '@/platform/workflow/validation/schemas/workflowSchema'
@@ -182,6 +183,7 @@ export class NodeOperationsHelper {
position: DefaultGraphPositions.emptyLatentWidgetClick
})
const dialogInput = this.page.locator('.graphdialog input[type="text"]')
await expect(dialogInput).toBeVisible()
await dialogInput.click()
await dialogInput.fill('128')
await dialogInput.press('Enter')

View File

@@ -337,6 +337,7 @@ export class SubgraphHelper {
const breadcrumb = this.page.getByTestId(TestIds.breadcrumb.subgraph)
const parentLink = breadcrumb.getByRole('link').first()
if (await parentLink.isVisible()) {
await expect(parentLink).toBeVisible()
await parentLink.click()
} else {
await this.page.evaluate(() => {

View File

@@ -30,6 +30,7 @@ export class ToastHelper {
.locator('.p-toast-close-button')
.all()
for (const button of toastCloseButtons) {
await expect(button).toBeVisible()
await button.click()
}

View File

@@ -1,3 +1,4 @@
import { expect } from '@playwright/test'
import { readFileSync } from 'fs'
import type { AppMode } from '@/composables/useAppMode'
@@ -90,9 +91,11 @@ export class WorkflowHelper {
}
// Delete workflow
await expect(workflowsTab.getPersistedItem(workflowName)).toBeVisible()
await workflowsTab.getPersistedItem(workflowName).click({ button: 'right' })
await this.comfyPage.contextMenu.clickMenuItem('Delete')
await this.comfyPage.nextFrame()
await expect(this.comfyPage.confirmDialog.delete).toBeVisible()
await this.comfyPage.confirmDialog.delete.click()
// Clear toast & close tab

View File

@@ -431,6 +431,7 @@ export class NodeReference {
async clickContextMenuOption(optionText: string) {
await this.click('title', { button: 'right' })
const ctx = this.comfyPage.page.locator('.litecontextmenu')
await expect(ctx.getByText(optionText)).toBeVisible()
await ctx.getByText(optionText).click()
}
async convertToGroupNode(groupNodeName: string = 'GroupNode') {

View File

@@ -1,4 +1,5 @@
import type { Locator } from '@playwright/test'
import { expect } from '@playwright/test'
import { TestIds } from '@e2e/fixtures/selectors'
@@ -58,6 +59,7 @@ export class VueNodeFixture {
}
async toggleCollapse(): Promise<void> {
await expect(this.collapseButton).toBeVisible()
await this.collapseButton.click()
}

View File

@@ -87,6 +87,7 @@ test.describe('App mode dropdown clipping', { tag: '@ui' }, () => {
// Click the codec select (combobox role with aria-label from WidgetSelectDefault)
const codecSelect = widgetList.getByRole('combobox', { name: 'codec' })
await expect(codecSelect).toBeVisible()
await codecSelect.click()
const overlay = comfyPage.page.locator('.p-select-overlay').first()
@@ -135,6 +136,7 @@ test.describe('App mode dropdown clipping', { tag: '@ui' }, () => {
'div:has(> div > span:text-is("image"))'
)
const dropdownButton = imageRow.locator('button:has(> span)').first()
await expect(dropdownButton).toBeVisible()
await dropdownButton.click()
// The unstyled PrimeVue Popover renders with role="dialog".

View File

@@ -25,6 +25,7 @@ test.describe('Background Image Upload', () => {
// Navigate to Appearance category
const appearanceOption = comfyPage.page.locator('text=Appearance')
await expect(appearanceOption).toBeVisible()
await appearanceOption.click()
// Find the background image setting
@@ -58,6 +59,7 @@ test.describe('Background Image Upload', () => {
// Navigate to Appearance category
const appearanceOption = comfyPage.page.locator('text=Appearance')
await expect(appearanceOption).toBeVisible()
await appearanceOption.click()
// Find the background image setting
@@ -71,6 +73,7 @@ test.describe('Background Image Upload', () => {
// Set up file upload handler
const fileChooserPromise = comfyPage.page.waitForEvent('filechooser')
await expect(uploadButton).toBeVisible()
await uploadButton.click()
const fileChooser = await fileChooserPromise
@@ -104,6 +107,7 @@ test.describe('Background Image Upload', () => {
// Navigate to Appearance category
const appearanceOption = comfyPage.page.locator('text=Appearance')
await expect(appearanceOption).toBeVisible()
await appearanceOption.click()
// Find the background image setting
@@ -146,6 +150,7 @@ test.describe('Background Image Upload', () => {
// Navigate to Appearance category
const appearanceOption = comfyPage.page.locator('text=Appearance')
await expect(appearanceOption).toBeVisible()
await appearanceOption.click()
// Find the background image setting
@@ -163,6 +168,7 @@ test.describe('Background Image Upload', () => {
await expect(clearButton).toBeEnabled()
// Click the clear button
await expect(clearButton).toBeVisible()
await clearButton.click()
// Verify the input is now empty
@@ -186,6 +192,7 @@ test.describe('Background Image Upload', () => {
// Navigate to Appearance category
const appearanceOption = comfyPage.page.locator('text=Appearance')
await expect(appearanceOption).toBeVisible()
await appearanceOption.click()
// Find the background image setting
@@ -227,6 +234,7 @@ test.describe('Background Image Upload', () => {
// Navigate to Appearance category
const appearanceOption = comfyPage.page.locator('text=Appearance')
await expect(appearanceOption).toBeVisible()
await appearanceOption.click()
// Find the background image setting
@@ -254,6 +262,7 @@ test.describe('Background Image Upload', () => {
await expect(clearButton).toBeEnabled()
// Use clear button - should clear input and disable itself
await expect(clearButton).toBeVisible()
await clearButton.click()
await expect(urlInput).toHaveValue('')
await expect(clearButton).toBeDisabled()

View File

@@ -12,6 +12,7 @@ test.describe('Bottom Panel Logs', { tag: '@ui' }, () => {
const { bottomPanel } = comfyPage
await expect(bottomPanel.root).not.toBeVisible()
await expect(bottomPanel.toggleButton).toBeVisible()
await bottomPanel.toggleButton.click()
await expect(bottomPanel.root).toBeVisible()
})
@@ -21,6 +22,7 @@ test.describe('Bottom Panel Logs', { tag: '@ui' }, () => {
}) => {
const { bottomPanel } = comfyPage
await expect(bottomPanel.toggleButton).toBeVisible()
await bottomPanel.toggleButton.click()
await expect(bottomPanel.root).toBeVisible()
@@ -31,6 +33,7 @@ test.describe('Bottom Panel Logs', { tag: '@ui' }, () => {
test('should close bottom panel via toggle button', async ({ comfyPage }) => {
const { bottomPanel } = comfyPage
await expect(bottomPanel.toggleButton).toBeVisible()
await bottomPanel.toggleButton.click()
await expect(bottomPanel.root).toBeVisible()
@@ -43,12 +46,14 @@ test.describe('Bottom Panel Logs', { tag: '@ui' }, () => {
}) => {
const { bottomPanel } = comfyPage
await expect(bottomPanel.keyboardShortcutsButton).toBeVisible()
await bottomPanel.keyboardShortcutsButton.click()
await expect(bottomPanel.root).toBeVisible()
await expect(
comfyPage.page.locator('[id*="tab_shortcuts-essentials"]')
).toBeVisible()
await expect(bottomPanel.toggleButton).toBeVisible()
await bottomPanel.toggleButton.click()
const logsTab = comfyPage.page.getByRole('tab', { name: /Logs/i })
@@ -63,6 +68,7 @@ test.describe('Bottom Panel Logs', { tag: '@ui' }, () => {
}) => {
const { bottomPanel } = comfyPage
await expect(bottomPanel.toggleButton).toBeVisible()
await bottomPanel.toggleButton.click()
await expect(bottomPanel.root).toBeVisible()
@@ -84,6 +90,7 @@ test.describe('Bottom Panel Logs', { tag: '@ui' }, () => {
}) => {
const { bottomPanel } = comfyPage
await expect(bottomPanel.toggleButton).toBeVisible()
await bottomPanel.toggleButton.click()
await expect(bottomPanel.root).toBeVisible()

View File

@@ -11,6 +11,7 @@ test.describe('Bottom Panel Shortcuts', { tag: '@ui' }, () => {
const { bottomPanel } = comfyPage
await expect(bottomPanel.root).not.toBeVisible()
await expect(bottomPanel.keyboardShortcutsButton).toBeVisible()
await bottomPanel.keyboardShortcutsButton.click()
await expect(bottomPanel.root).toBeVisible()
await bottomPanel.keyboardShortcutsButton.click()
@@ -20,6 +21,7 @@ test.describe('Bottom Panel Shortcuts', { tag: '@ui' }, () => {
test('should display essentials shortcuts tab', async ({ comfyPage }) => {
const { bottomPanel } = comfyPage
await expect(bottomPanel.keyboardShortcutsButton).toBeVisible()
await bottomPanel.keyboardShortcutsButton.click()
await expect(bottomPanel.shortcuts.essentialsTab).toBeVisible()
@@ -45,7 +47,9 @@ test.describe('Bottom Panel Shortcuts', { tag: '@ui' }, () => {
test('should display view controls shortcuts tab', async ({ comfyPage }) => {
const { bottomPanel } = comfyPage
await expect(bottomPanel.keyboardShortcutsButton).toBeVisible()
await bottomPanel.keyboardShortcutsButton.click()
await expect(bottomPanel.shortcuts.viewControlsTab).toBeVisible()
await bottomPanel.shortcuts.viewControlsTab.click()
await expect(bottomPanel.shortcuts.viewControlsTab).toHaveAttribute(
@@ -66,6 +70,7 @@ test.describe('Bottom Panel Shortcuts', { tag: '@ui' }, () => {
test('should switch between shortcuts tabs', async ({ comfyPage }) => {
const { bottomPanel } = comfyPage
await expect(bottomPanel.keyboardShortcutsButton).toBeVisible()
await bottomPanel.keyboardShortcutsButton.click()
await expect(bottomPanel.shortcuts.essentialsTab).toHaveAttribute(
@@ -73,6 +78,7 @@ test.describe('Bottom Panel Shortcuts', { tag: '@ui' }, () => {
'true'
)
await expect(bottomPanel.shortcuts.viewControlsTab).toBeVisible()
await bottomPanel.shortcuts.viewControlsTab.click()
await expect(bottomPanel.shortcuts.viewControlsTab).toHaveAttribute(
@@ -84,6 +90,7 @@ test.describe('Bottom Panel Shortcuts', { tag: '@ui' }, () => {
'true'
)
await expect(bottomPanel.shortcuts.essentialsTab).toBeVisible()
await bottomPanel.shortcuts.essentialsTab.click()
await expect(bottomPanel.shortcuts.essentialsTab).toHaveAttribute(
@@ -99,6 +106,7 @@ test.describe('Bottom Panel Shortcuts', { tag: '@ui' }, () => {
test('should display formatted keyboard shortcuts', async ({ comfyPage }) => {
const { bottomPanel } = comfyPage
await expect(bottomPanel.keyboardShortcutsButton).toBeVisible()
await bottomPanel.keyboardShortcutsButton.click()
const keyBadges = bottomPanel.shortcuts.keyBadges
@@ -119,6 +127,7 @@ test.describe('Bottom Panel Shortcuts', { tag: '@ui' }, () => {
const { bottomPanel } = comfyPage
// Open shortcuts panel first
await expect(bottomPanel.keyboardShortcutsButton).toBeVisible()
await bottomPanel.keyboardShortcutsButton.click()
await expect(bottomPanel.root).toBeVisible()
await expect(
@@ -127,6 +136,7 @@ test.describe('Bottom Panel Shortcuts', { tag: '@ui' }, () => {
// Try to open terminal panel - may show terminal OR close shortcuts
// depending on whether terminal tabs have loaded (async loading)
await expect(bottomPanel.toggleButton).toBeVisible()
await bottomPanel.toggleButton.click()
// Check if terminal tabs loaded (Logs tab visible) or fell back to shortcuts toggle
@@ -138,6 +148,7 @@ test.describe('Bottom Panel Shortcuts', { tag: '@ui' }, () => {
await expect(bottomPanel.root).toBeVisible()
// Switch back to shortcuts
await expect(bottomPanel.keyboardShortcutsButton).toBeVisible()
await bottomPanel.keyboardShortcutsButton.click()
// Should show shortcuts content again
@@ -146,6 +157,7 @@ test.describe('Bottom Panel Shortcuts', { tag: '@ui' }, () => {
).toBeVisible()
} else {
// Terminal tabs not loaded - button toggled shortcuts off, reopen for verification
await expect(bottomPanel.keyboardShortcutsButton).toBeVisible()
await bottomPanel.keyboardShortcutsButton.click()
await expect(bottomPanel.root).toBeVisible()
await expect(
@@ -157,6 +169,7 @@ test.describe('Bottom Panel Shortcuts', { tag: '@ui' }, () => {
test('should handle keyboard navigation', async ({ comfyPage }) => {
const { bottomPanel } = comfyPage
await expect(bottomPanel.keyboardShortcutsButton).toBeVisible()
await bottomPanel.keyboardShortcutsButton.click()
await bottomPanel.shortcuts.essentialsTab.focus()
@@ -177,6 +190,7 @@ test.describe('Bottom Panel Shortcuts', { tag: '@ui' }, () => {
}) => {
const { bottomPanel } = comfyPage
await expect(bottomPanel.keyboardShortcutsButton).toBeVisible()
await bottomPanel.keyboardShortcutsButton.click()
await expect(bottomPanel.root).toBeVisible()
@@ -189,6 +203,7 @@ test.describe('Bottom Panel Shortcuts', { tag: '@ui' }, () => {
}) => {
const { bottomPanel } = comfyPage
await expect(bottomPanel.keyboardShortcutsButton).toBeVisible()
await bottomPanel.keyboardShortcutsButton.click()
await expect(
@@ -221,6 +236,7 @@ test.describe('Bottom Panel Shortcuts', { tag: '@ui' }, () => {
}) => {
const { bottomPanel } = comfyPage
await expect(bottomPanel.keyboardShortcutsButton).toBeVisible()
await bottomPanel.keyboardShortcutsButton.click()
await expect(bottomPanel.shortcuts.manageButton).toBeVisible()

View File

@@ -20,6 +20,7 @@ async function saveCloseAndReopenAsApp(
) {
await appMode.steps.goToPreview()
await builderSaveAs(appMode, workflowName)
await expect(appMode.saveAs.closeButton).toBeVisible()
await appMode.saveAs.closeButton.click()
await comfyPage.nextFrame()

View File

@@ -36,6 +36,7 @@ test.describe('Builder save flow', { tag: ['@ui'] }, () => {
test('Save as dialog appears for unsaved workflow', async ({ comfyPage }) => {
const { saveAs } = comfyPage.appMode
await setupBuilder(comfyPage)
await expect(comfyPage.appMode.footer.saveAsButton).toBeVisible()
await comfyPage.appMode.footer.saveAsButton.click()
await expect(saveAs.dialog).toBeVisible({ timeout: 5000 })
@@ -56,6 +57,7 @@ test.describe('Builder save flow', { tag: ['@ui'] }, () => {
}) => {
const { saveAs } = comfyPage.appMode
await setupBuilder(comfyPage)
await expect(comfyPage.appMode.footer.saveAsButton).toBeVisible()
await comfyPage.appMode.footer.saveAsButton.click()
await expect(saveAs.dialog).toBeVisible({ timeout: 5000 })
@@ -66,6 +68,7 @@ test.describe('Builder save flow', { tag: ['@ui'] }, () => {
test('View type can be toggled in save-as dialog', async ({ comfyPage }) => {
const { saveAs } = comfyPage.appMode
await setupBuilder(comfyPage)
await expect(comfyPage.appMode.footer.saveAsButton).toBeVisible()
await comfyPage.appMode.footer.saveAsButton.click()
await expect(saveAs.dialog).toBeVisible({ timeout: 5000 })
@@ -74,6 +77,7 @@ test.describe('Builder save flow', { tag: ['@ui'] }, () => {
await expect(appRadio).toHaveAttribute('aria-checked', 'true')
const graphRadio = saveAs.viewTypeRadio('Node graph')
await expect(graphRadio).toBeVisible()
await graphRadio.click()
await expect(graphRadio).toHaveAttribute('aria-checked', 'true')
await expect(appRadio).toHaveAttribute('aria-checked', 'false')
@@ -121,6 +125,7 @@ test.describe('Builder save flow', { tag: ['@ui'] }, () => {
await setupBuilder(comfyPage)
await builderSaveAs(comfyPage.appMode, `${Date.now()} direct-save`, 'App')
await expect(saveAs.closeButton).toBeVisible()
await saveAs.closeButton.click()
await comfyPage.nextFrame()
@@ -129,6 +134,7 @@ test.describe('Builder save flow', { tag: ['@ui'] }, () => {
await comfyPage.appMode.select.deleteInput('seed')
await expect(footer.saveButton).toBeEnabled({ timeout: 5000 })
await expect(footer.saveButton).toBeVisible()
await footer.saveButton.click()
await comfyPage.nextFrame()
@@ -143,6 +149,7 @@ test.describe('Builder save flow', { tag: ['@ui'] }, () => {
await setupBuilder(comfyPage)
await builderSaveAs(comfyPage.appMode, `${Date.now()} split-btn`, 'App')
await expect(saveAs.closeButton).toBeVisible()
await saveAs.closeButton.click()
await comfyPage.nextFrame()
@@ -177,6 +184,7 @@ test.describe('Builder save flow', { tag: ['@ui'] }, () => {
// Save the workflow to transition to the Save + chevron state
await builderSaveAs(appMode, `${Date.now()} width-test`, 'App')
await expect(appMode.saveAs.closeButton).toBeVisible()
await appMode.saveAs.closeButton.click()
await comfyPage.nextFrame()
@@ -193,6 +201,7 @@ test.describe('Builder save flow', { tag: ['@ui'] }, () => {
await fitToViewInstant(comfyPage)
await comfyPage.appMode.enterBuilder()
await expect(comfyPage.appMode.footer.saveAsButton).toBeVisible()
await comfyPage.appMode.footer.saveAsButton.click()
await expect(
@@ -235,6 +244,7 @@ test.describe('Builder save flow', { tag: ['@ui'] }, () => {
await setupBuilder(comfyPage)
await builderSaveAs(comfyPage.appMode, `${Date.now()} app-view`, 'App')
await expect(comfyPage.appMode.saveAs.viewAppButton).toBeVisible()
await comfyPage.appMode.saveAs.viewAppButton.click()
await comfyPage.nextFrame()
@@ -253,6 +263,7 @@ test.describe('Builder save flow', { tag: ['@ui'] }, () => {
'Node graph'
)
await expect(comfyPage.appMode.saveAs.exitBuilderButton).toBeVisible()
await comfyPage.appMode.saveAs.exitBuilderButton.click()
await comfyPage.nextFrame()
@@ -269,6 +280,7 @@ test.describe('Builder save flow', { tag: ['@ui'] }, () => {
await builderSaveAs(appMode, originalName, 'App')
const originalPath = await comfyPage.workflow.getActiveWorkflowPath()
expect(originalPath).toContain('.app.json')
await expect(appMode.saveAs.closeButton).toBeVisible()
await appMode.saveAs.closeButton.click()
await comfyPage.nextFrame()
@@ -281,6 +293,7 @@ test.describe('Builder save flow', { tag: ['@ui'] }, () => {
expect(newPath).not.toContain('.app.json')
// Dismiss success dialog, exit app mode, reopen the original
await expect(appMode.saveAs.dismissButton).toBeVisible()
await appMode.saveAs.dismissButton.click()
await comfyPage.nextFrame()
await appMode.toggleAppMode()
@@ -298,6 +311,7 @@ test.describe('Builder save flow', { tag: ['@ui'] }, () => {
await setupBuilder(comfyPage)
await builderSaveAs(appMode, name, 'App')
await expect(appMode.saveAs.closeButton).toBeVisible()
await appMode.saveAs.closeButton.click()
await comfyPage.nextFrame()
@@ -306,6 +320,7 @@ test.describe('Builder save flow', { tag: ['@ui'] }, () => {
await reSaveAs(appMode, name, 'App')
await expect(appMode.saveAs.overwriteDialog).toBeVisible({ timeout: 5000 })
await expect(appMode.saveAs.overwriteButton).toBeVisible()
await appMode.saveAs.overwriteButton.click()
await expect(appMode.saveAs.successMessage).toBeVisible({ timeout: 5000 })
@@ -324,6 +339,7 @@ test.describe('Builder save flow', { tag: ['@ui'] }, () => {
await builderSaveAs(appMode, name, 'App')
const pathAfterFirst = await comfyPage.workflow.getActiveWorkflowPath()
expect(pathAfterFirst).toContain('.app.json')
await expect(appMode.saveAs.closeButton).toBeVisible()
await appMode.saveAs.closeButton.click()
await comfyPage.nextFrame()
@@ -340,6 +356,7 @@ test.describe('Builder save flow', { tag: ['@ui'] }, () => {
const name = `${Date.now()} reload-app`
await setupBuilder(comfyPage)
await builderSaveAs(comfyPage.appMode, name, 'App')
await expect(comfyPage.appMode.saveAs.dismissButton).toBeVisible()
await comfyPage.appMode.saveAs.dismissButton.click()
await comfyPage.nextFrame()
await comfyPage.appMode.footer.exitBuilder()
@@ -356,6 +373,7 @@ test.describe('Builder save flow', { tag: ['@ui'] }, () => {
const name = `${Date.now()} reload-graph`
await setupBuilder(comfyPage)
await builderSaveAs(comfyPage.appMode, name, 'Node graph')
await expect(comfyPage.appMode.saveAs.dismissButton).toBeVisible()
await comfyPage.appMode.saveAs.dismissButton.click()
await comfyPage.nextFrame()
await comfyPage.appMode.toggleAppMode()

View File

@@ -47,17 +47,20 @@ test.describe(
// Switch back to tab 0 (workflow-a).
const tab0 = comfyPage.menu.topbar.getWorkflowTab('workflow-a')
await expect(tab0).toBeVisible()
await tab0.click()
await comfyPage.nextFrame()
expect(await comfyPage.nodeOps.getGraphNodesCount()).toBe(7)
// switch to blank tab and back to verify no corruption
const tab1 = comfyPage.menu.topbar.getWorkflowTab('Unsaved Workflow')
await expect(tab1).toBeVisible()
await tab1.click()
await comfyPage.nextFrame()
expect(await comfyPage.nodeOps.getGraphNodesCount()).toBe(0)
// switch again and verify no corruption
await expect(tab0).toBeVisible()
await tab0.click()
await comfyPage.nextFrame()
expect(await comfyPage.nodeOps.getGraphNodesCount()).toBe(7)

View File

@@ -32,6 +32,7 @@ test.describe('Copy Paste', { tag: ['@screenshot', '@workflow'] }, () => {
test('Can copy and paste text', async ({ comfyPage }) => {
const textBox = comfyPage.widgetTextBox
await expect(textBox).toBeVisible()
await textBox.click()
const originalString = await textBox.inputValue()
await textBox.selectText()
@@ -78,6 +79,7 @@ test.describe('Copy Paste', { tag: ['@screenshot', '@workflow'] }, () => {
await comfyPage.nextFrame()
await comfyPage.clipboard.copy(null)
const textBox = comfyPage.widgetTextBox
await expect(textBox).toBeVisible()
await textBox.click()
await textBox.inputValue()
await textBox.selectText()
@@ -91,6 +93,7 @@ test.describe('Copy Paste', { tag: ['@screenshot', '@workflow'] }, () => {
test('Copy text area does not copy node', async ({ comfyPage }) => {
const textBox = comfyPage.widgetTextBox
await expect(textBox).toBeVisible()
await textBox.click()
await textBox.inputValue()
await textBox.selectText()

View File

@@ -65,12 +65,14 @@ test.describe('Settings', () => {
const newBlankWorkflowRow = comfyPage.page.locator('tr', {
has: comfyPage.page.getByRole('cell', { name: 'New Blank Workflow' })
})
await expect(newBlankWorkflowRow).toBeVisible()
await newBlankWorkflowRow.click()
// Click add keybinding button (New Blank Workflow has no default keybinding)
const addKeybindingButton = newBlankWorkflowRow.locator(
'.icon-\\[lucide--plus\\]'
)
await expect(addKeybindingButton).toBeVisible()
await addKeybindingButton.click()
// Set new keybinding
@@ -88,6 +90,7 @@ test.describe('Settings', () => {
const saveButton = comfyPage.page
.getByLabel('Modify keybinding')
.getByText('Save')
await expect(saveButton).toBeVisible()
await saveButton.click()
const request = await requestPromise
@@ -142,6 +145,7 @@ test.describe('Signin dialog', () => {
await comfyPage.clipboard.copy()
const textBox = comfyPage.widgetTextBox
await expect(textBox).toBeVisible()
await textBox.click()
await textBox.fill('test_password')
await textBox.press('Control+a')

View File

@@ -6,6 +6,7 @@ import {
test.describe('Queue Clear History Dialog', { tag: '@ui' }, () => {
test.beforeEach(async ({ comfyPage }) => {
await comfyPage.setup()
await expect(comfyPage.queuePanel.overlayToggle).toBeVisible()
await comfyPage.queuePanel.overlayToggle.click()
})
@@ -59,6 +60,7 @@ test.describe('Queue Clear History Dialog', { tag: '@ui' }, () => {
return route.continue()
})
await expect(dialog.getByRole('button', { name: 'Cancel' })).toBeVisible()
await dialog.getByRole('button', { name: 'Cancel' }).click()
await expect(dialog).not.toBeVisible()
expect(clearCalled).toBe(false)
@@ -82,6 +84,7 @@ test.describe('Queue Clear History Dialog', { tag: '@ui' }, () => {
return route.continue()
})
await expect(dialog.getByLabel('Close')).toBeVisible()
await dialog.getByLabel('Close').click()
await expect(dialog).not.toBeVisible()
expect(clearCalled).toBe(false)
@@ -101,6 +104,7 @@ test.describe('Queue Clear History Dialog', { tag: '@ui' }, () => {
(req) => req.url().includes('/api/history') && req.method() === 'POST'
)
await expect(dialog.getByRole('button', { name: 'Clear' })).toBeVisible()
await dialog.getByRole('button', { name: 'Clear' }).click()
const request = await clearPromise
@@ -113,6 +117,7 @@ test.describe('Queue Clear History Dialog', { tag: '@ui' }, () => {
await comfyPage.queuePanel.openClearHistoryDialog()
const dialog = comfyPage.confirmDialog.root
await expect(dialog).toBeVisible()
await expect(dialog.getByRole('button', { name: 'Cancel' })).toBeVisible()
await dialog.getByRole('button', { name: 'Cancel' }).click()
await expect(dialog).not.toBeVisible()

View File

@@ -22,6 +22,7 @@ test.describe('Sign In dialog', { tag: '@ui' }, () => {
})
test('Should toggle from sign-in to sign-up form', async () => {
await expect(dialog.signUpLink).toBeVisible()
await dialog.signUpLink.click()
await expect(
@@ -34,11 +35,13 @@ test.describe('Sign In dialog', { tag: '@ui' }, () => {
})
test('Should toggle back from sign-up to sign-in form', async () => {
await expect(dialog.signUpLink).toBeVisible()
await dialog.signUpLink.click()
await expect(
dialog.root.getByRole('heading', { name: 'Create an account' })
).toBeVisible()
await expect(dialog.signInLink).toBeVisible()
await dialog.signInLink.click()
await expect(
dialog.root.getByRole('heading', { name: 'Log in to your account' })
@@ -48,11 +51,13 @@ test.describe('Sign In dialog', { tag: '@ui' }, () => {
})
test('Should navigate to the API Key form and back', async () => {
await expect(dialog.apiKeyButton).toBeVisible()
await dialog.apiKeyButton.click()
await expect(dialog.apiKeyHeading).toBeVisible()
await expect(dialog.apiKeyInput).toBeVisible()
await expect(dialog.backButton).toBeVisible()
await dialog.backButton.click()
await expect(
dialog.root.getByRole('heading', { name: 'Log in to your account' })
@@ -83,6 +88,7 @@ test.describe('Sign In dialog', { tag: '@ui' }, () => {
})
test('Should close dialog via close button', async () => {
await expect(dialog.closeButton).toBeVisible()
await dialog.closeButton.click()
await expect(dialog.root).toBeHidden()
})

View File

@@ -76,6 +76,9 @@ test.describe('Error dialog', () => {
await expect(errorDialog).toBeVisible()
await expect(errorDialog.locator('pre')).not.toBeVisible()
await expect(
errorDialog.getByTestId(TestIds.dialogs.errorDialogShowReport)
).toBeVisible()
await errorDialog.getByTestId(TestIds.dialogs.errorDialogShowReport).click()
const reportPre = errorDialog.locator('pre')
@@ -92,11 +95,17 @@ test.describe('Error dialog', () => {
const errorDialog = await triggerConfigureError(comfyPage)
await expect(errorDialog).toBeVisible()
await expect(
errorDialog.getByTestId(TestIds.dialogs.errorDialogShowReport)
).toBeVisible()
await errorDialog.getByTestId(TestIds.dialogs.errorDialogShowReport).click()
await expect(errorDialog.locator('pre')).toBeVisible()
await interceptClipboardWrite(comfyPage.page)
await expect(
errorDialog.getByTestId(TestIds.dialogs.errorDialogCopyReport)
).toBeVisible()
await errorDialog.getByTestId(TestIds.dialogs.errorDialogCopyReport).click()
const reportText = await errorDialog.locator('pre').textContent()

View File

@@ -149,6 +149,9 @@ test.describe('Error overlay', { tag: '@ui' }, () => {
const overlay = getOverlay(comfyPage.page)
await expect(overlay).toBeVisible()
await expect(
overlay.getByTestId(TestIds.dialogs.errorOverlaySeeErrors)
).toBeVisible()
await overlay.getByTestId(TestIds.dialogs.errorOverlaySeeErrors).click()
await expect(comfyPage.page.getByTestId('properties-panel')).toBeVisible()
@@ -160,6 +163,9 @@ test.describe('Error overlay', { tag: '@ui' }, () => {
const overlay = getOverlay(comfyPage.page)
await expect(overlay).toBeVisible()
await expect(
overlay.getByTestId(TestIds.dialogs.errorOverlaySeeErrors)
).toBeVisible()
await overlay.getByTestId(TestIds.dialogs.errorOverlaySeeErrors).click()
await expect(overlay).not.toBeVisible()
@@ -173,6 +179,9 @@ test.describe('Error overlay', { tag: '@ui' }, () => {
const overlay = getOverlay(comfyPage.page)
await expect(overlay).toBeVisible()
await expect(
overlay.getByTestId(TestIds.dialogs.errorOverlayDismiss)
).toBeVisible()
await overlay.getByTestId(TestIds.dialogs.errorOverlayDismiss).click()
await expect(overlay).not.toBeVisible()
@@ -187,6 +196,9 @@ test.describe('Error overlay', { tag: '@ui' }, () => {
const overlay = getOverlay(comfyPage.page)
await expect(overlay).toBeVisible()
await expect(
overlay.getByRole('button', { name: /close/i })
).toBeVisible()
await overlay.getByRole('button', { name: /close/i }).click()
await expect(overlay).not.toBeVisible()

View File

@@ -361,6 +361,7 @@ test.describe('Topbar commands', () => {
const toolboxButton = comfyPage.page.locator(
'.selection-toolbox button:has(.pi-star)'
)
await expect(toolboxButton).toBeVisible()
await toolboxButton.click()
expect(

View File

@@ -23,6 +23,7 @@ test.describe('Graph Canvas Menu', { tag: ['@screenshot', '@canvas'] }, () => {
const button = comfyPage.page.getByTestId(
TestIds.canvas.toggleLinkVisibilityButton
)
await expect(button).toBeVisible()
await button.click()
await comfyPage.nextFrame()
await expect(comfyPage.canvas).toHaveScreenshot(
@@ -35,6 +36,7 @@ test.describe('Graph Canvas Menu', { tag: ['@screenshot', '@canvas'] }, () => {
hiddenLinkRenderMode
)
await expect(button).toBeVisible()
await button.click()
await comfyPage.nextFrame()
await expect(comfyPage.canvas).toHaveScreenshot(
@@ -91,6 +93,7 @@ test.describe('Graph Canvas Menu', { tag: ['@screenshot', '@canvas'] }, () => {
// Click backdrop to close
const backdrop = comfyPage.page.locator('.fixed.inset-0').first()
await expect(backdrop).toBeVisible()
await backdrop.click()
await comfyPage.nextFrame()

View File

@@ -41,7 +41,9 @@ test.describe('Group Node', { tag: '@node' }, () => {
const initialNodeCount = await comfyPage.nodeOps.getGraphNodesCount()
// Add group node from node library sidebar
await expect(libraryTab.getFolder(groupNodeCategory)).toBeVisible()
await libraryTab.getFolder(groupNodeCategory).click()
await expect(libraryTab.getNode(groupNodeName)).toBeVisible()
await libraryTab.getNode(groupNodeName).click()
// Verify the node is added to the canvas
@@ -51,6 +53,7 @@ test.describe('Group Node', { tag: '@node' }, () => {
})
test('Can be bookmarked and unbookmarked', async ({ comfyPage }) => {
await expect(libraryTab.getFolder(groupNodeCategory)).toBeVisible()
await libraryTab.getFolder(groupNodeCategory).click()
await libraryTab
.getNode(groupNodeName)
@@ -78,6 +81,7 @@ test.describe('Group Node', { tag: '@node' }, () => {
})
test('Displays preview on bookmark hover', async ({ comfyPage }) => {
await expect(libraryTab.getFolder(groupNodeCategory)).toBeVisible()
await libraryTab.getFolder(groupNodeCategory).click()
await libraryTab
.getNode(groupNodeName)

View File

@@ -43,6 +43,7 @@ test.describe('Item Interaction', { tag: ['@screenshot', '@node'] }, () => {
test.describe('Node Interaction', () => {
test('Can enter prompt', async ({ comfyPage }) => {
const textBox = comfyPage.widgetTextBox
await expect(textBox).toBeVisible()
await textBox.click()
await textBox.fill('Hello World')
await expect(textBox).toHaveValue('Hello World')
@@ -684,6 +685,7 @@ test.describe('Canvas Interaction', { tag: '@screenshot' }, () => {
test.describe('Widget Interaction', () => {
test('Undo text input', async ({ comfyPage }) => {
const textBox = comfyPage.widgetTextBox
await expect(textBox).toBeVisible()
await textBox.click()
await textBox.fill('')
await expect(textBox).toHaveValue('')
@@ -696,6 +698,7 @@ test.describe('Widget Interaction', () => {
test('Undo attention edit', async ({ comfyPage }) => {
await comfyPage.settings.setSetting('Comfy.EditAttention.Delta', 0.05)
const textBox = comfyPage.widgetTextBox
await expect(textBox).toBeVisible()
await textBox.click()
await textBox.fill('1girl')
await expect(textBox).toHaveValue('1girl')
@@ -964,6 +967,7 @@ test.describe('Viewport settings', () => {
comfyMouse
}) => {
const changeTab = async (tab: Locator) => {
await expect(tab).toBeVisible()
await tab.click()
await comfyPage.nextFrame()
await comfyMouse.move(DefaultGraphPositions.emptySpace)
@@ -980,6 +984,7 @@ test.describe('Viewport settings', () => {
const toggleButton = comfyPage.page.getByTestId(
TestIds.canvas.toggleMinimapButton
)
await expect(toggleButton).toBeVisible()
await toggleButton.click()
await comfyPage.settings.setSetting('Comfy.Graph.CanvasMenu', false)

View File

@@ -11,6 +11,9 @@ test.describe('Job History Actions', { tag: '@ui' }, () => {
await comfyPage.setup()
// Expand the queue overlay so the JobHistoryActionsMenu is visible
await expect(
comfyPage.page.getByTestId('queue-overlay-toggle')
).toBeVisible()
await comfyPage.page.getByTestId('queue-overlay-toggle').click()
})
@@ -18,6 +21,7 @@ test.describe('Job History Actions', { tag: '@ui' }, () => {
page: { getByLabel(label: string | RegExp): Locator }
}) {
const moreButton = comfyPage.page.getByLabel(/More options/i).first()
await expect(moreButton).toBeVisible()
await moreButton.click()
}
@@ -81,6 +85,7 @@ test.describe('Job History Actions', { tag: '@ui' }, () => {
const action = comfyPage.page.locator(
'[data-testid="show-run-progress-bar-action"]'
)
await expect(action).toBeVisible()
await action.click()
const settingAfter = await comfyPage.settings.getSetting<boolean>(

View File

@@ -27,9 +27,13 @@ const TEST_PRESET = {
async function importPreset(page: Page, preset: typeof TEST_PRESET) {
const menuButton = page.getByTestId('keybinding-preset-menu')
await expect(menuButton).toBeVisible()
await menuButton.click()
const fileChooserPromise = page.waitForEvent('filechooser')
await expect(
page.getByRole('menuitem', { name: /Import preset/i })
).toBeVisible()
await page.getByRole('menuitem', { name: /Import preset/i }).click()
const fileChooser = await fileChooserPromise
@@ -68,6 +72,7 @@ test.describe('Keybinding Presets', { tag: '@keyboard' }, () => {
// Open keybinding settings panel
await comfyPage.settingDialog.open()
await expect(comfyPage.settingDialog.category('Keybinding')).toBeVisible()
await comfyPage.settingDialog.category('Keybinding').click()
await importPreset(page, TEST_PRESET)
@@ -96,9 +101,14 @@ test.describe('Keybinding Presets', { tag: '@keyboard' }, () => {
// Switch back to default preset
await comfyPage.settingDialog.open()
await expect(comfyPage.settingDialog.category('Keybinding')).toBeVisible()
await comfyPage.settingDialog.category('Keybinding').click()
await expect(presetTrigger).toBeVisible()
await presetTrigger.click()
await expect(
page.getByRole('option', { name: /Default Preset/i })
).toBeVisible()
await page.getByRole('option', { name: /Default Preset/i }).click()
// Handle unsaved changes dialog if the preset was marked as modified
@@ -106,6 +116,7 @@ test.describe('Keybinding Presets', { tag: '@keyboard' }, () => {
name: /Discard and Switch/i
})
if (await discardButton.isVisible({ timeout: 2000 }).catch(() => false)) {
await expect(discardButton).toBeVisible()
await discardButton.click()
}
@@ -122,6 +133,7 @@ test.describe('Keybinding Presets', { tag: '@keyboard' }, () => {
// Open keybinding settings panel
await comfyPage.settingDialog.open()
await expect(comfyPage.settingDialog.category('Keybinding')).toBeVisible()
await comfyPage.settingDialog.category('Keybinding').click()
await importPreset(page, TEST_PRESET)
@@ -138,8 +150,12 @@ test.describe('Keybinding Presets', { tag: '@keyboard' }, () => {
})
// Export via ellipsis menu
await expect(menuButton).toBeVisible()
await menuButton.click()
const downloadPromise = page.waitForEvent('download')
await expect(
page.getByRole('menuitem', { name: /Export preset/i })
).toBeVisible()
await page.getByRole('menuitem', { name: /Export preset/i }).click()
const download = await downloadPromise
@@ -172,6 +188,7 @@ test.describe('Keybinding Presets', { tag: '@keyboard' }, () => {
// Open keybinding settings panel
await comfyPage.settingDialog.open()
await expect(comfyPage.settingDialog.category('Keybinding')).toBeVisible()
await comfyPage.settingDialog.category('Keybinding').click()
await importPreset(page, TEST_PRESET)
@@ -188,13 +205,20 @@ test.describe('Keybinding Presets', { tag: '@keyboard' }, () => {
})
// Delete via ellipsis menu
await expect(menuButton).toBeVisible()
await menuButton.click()
await expect(
page.getByRole('menuitem', { name: /Delete preset/i })
).toBeVisible()
await page.getByRole('menuitem', { name: /Delete preset/i }).click()
// Confirm deletion in the dialog
const confirmDialog = page.getByRole('dialog', {
name: /Delete the current preset/i
})
await expect(
confirmDialog.getByRole('button', { name: /Delete/i })
).toBeVisible()
await confirmDialog.getByRole('button', { name: /Delete/i }).click()
// Verify preset trigger now shows Default Preset
@@ -212,6 +236,7 @@ test.describe('Keybinding Presets', { tag: '@keyboard' }, () => {
// Open keybinding settings panel
await comfyPage.settingDialog.open()
await expect(comfyPage.settingDialog.category('Keybinding')).toBeVisible()
await comfyPage.settingDialog.category('Keybinding').click()
await importPreset(page, TEST_PRESET)
@@ -228,7 +253,11 @@ test.describe('Keybinding Presets', { tag: '@keyboard' }, () => {
})
// Save as new preset via ellipsis menu
await expect(menuButton).toBeVisible()
await menuButton.click()
await expect(
page.getByRole('menuitem', { name: /Save as new preset/i })
).toBeVisible()
await page.getByRole('menuitem', { name: /Save as new preset/i }).click()
// Fill in the preset name in the prompt dialog

View File

@@ -15,6 +15,7 @@ test.describe('Keybindings', { tag: '@keyboard' }, () => {
})
const textBox = comfyPage.widgetTextBox
await expect(textBox).toBeVisible()
await textBox.click()
await textBox.fill('k')
await expect(textBox).toHaveValue('k')
@@ -31,6 +32,7 @@ test.describe('Keybindings', { tag: '@keyboard' }, () => {
})
const textBox = comfyPage.widgetTextBox
await expect(textBox).toBeVisible()
await textBox.click()
await textBox.fill('q')
await textBox.press('Control+k')
@@ -46,6 +48,7 @@ test.describe('Keybindings', { tag: '@keyboard' }, () => {
})
const textBox = comfyPage.widgetTextBox
await expect(textBox).toBeVisible()
await textBox.click()
await textBox.press('Control+v')
await expect(textBox).toBeFocused()

View File

@@ -1,4 +1,5 @@
import type { Locator } from '@playwright/test'
import { expect } from '@playwright/test'
export class Load3DHelper {
constructor(readonly node: Locator) {}
@@ -28,6 +29,7 @@ export class Load3DHelper {
}
async openMenu(): Promise<void> {
await expect(this.menuButton).toBeVisible()
await this.menuButton.click()
}

View File

@@ -40,6 +40,9 @@ test.describe('Mask Editor', () => {
// Hover over the image panel to reveal action buttons
await imagePreview.getByRole('region').hover()
await expect(
comfyPage.page.getByLabel('Edit or mask image')
).toBeVisible()
await comfyPage.page.getByLabel('Edit or mask image').click()
const dialog = comfyPage.page.locator('.mask-editor-dialog')
@@ -70,12 +73,14 @@ test.describe('Mask Editor', () => {
const nodeHeader = comfyPage.vueNodes
.getNodeLocator(nodeId)
.locator('.lg-node-header')
await expect(nodeHeader).toBeVisible()
await nodeHeader.click()
await nodeHeader.click({ button: 'right' })
const contextMenu = comfyPage.page.locator('.p-contextmenu')
await expect(contextMenu).toBeVisible()
await expect(contextMenu.getByText('Open in Mask Editor')).toBeVisible()
await contextMenu.getByText('Open in Mask Editor').click()
const dialog = comfyPage.page.locator('.mask-editor-dialog')

View File

@@ -119,6 +119,7 @@ test.describe('Menu', { tag: '@ui' }, () => {
await expect(checkmark).not.toHaveClass(/invisible/)
// Click Bottom Panel again to toggle it off
await expect(bottomPanelItem).toBeVisible()
await bottomPanelItem.click()
// Verify menu is still visible after second click

View File

@@ -52,6 +52,7 @@ test.describe('Minimap', { tag: '@canvas' }, () => {
await expect(minimapContainer).toBeVisible()
await expect(toggleButton).toBeVisible()
await toggleButton.click()
await comfyPage.nextFrame()
@@ -83,6 +84,9 @@ test.describe('Minimap', { tag: '@canvas' }, () => {
const minimap = comfyPage.page.locator('.litegraph-minimap')
await expect(minimap).toBeVisible()
await expect(
comfyPage.page.getByTestId(TestIds.canvas.closeMinimapButton)
).toBeVisible()
await comfyPage.page.getByTestId(TestIds.canvas.closeMinimapButton).click()
await expect(minimap).not.toBeVisible()

View File

@@ -174,6 +174,7 @@ test.describe('Node Help', { tag: ['@slow', '@ui'] }, () => {
const helpButton = ksamplerNode.getByRole('button', {
name: /learn more/i
})
await expect(helpButton).toBeVisible()
await helpButton.click()
// Verify help page is shown
@@ -182,6 +183,7 @@ test.describe('Node Help', { tag: ['@slow', '@ui'] }, () => {
// Click the back button - use a more specific selector
const backButton = helpPage.getByRole('button', { name: /back/i })
await expect(backButton).toBeVisible()
await backButton.click()
// Verify that we're back to the node library view

View File

@@ -37,6 +37,7 @@ test.describe('Node Library Essentials Tab', { tag: '@ui' }, () => {
test('Node library opens via sidebar', async ({ comfyPage }) => {
const tabButton = comfyPage.page.locator('.node-library-tab-button')
await expect(tabButton).toBeVisible()
await tabButton.click()
const sidebarContent = comfyPage.page.locator(
@@ -47,6 +48,7 @@ test.describe('Node Library Essentials Tab', { tag: '@ui' }, () => {
test('Essentials tab is visible in node library', async ({ comfyPage }) => {
const tabButton = comfyPage.page.locator('.node-library-tab-button')
await expect(tabButton).toBeVisible()
await tabButton.click()
const essentialsTab = comfyPage.page.getByRole('tab', {
@@ -59,11 +61,13 @@ test.describe('Node Library Essentials Tab', { tag: '@ui' }, () => {
comfyPage
}) => {
const tabButton = comfyPage.page.locator('.node-library-tab-button')
await expect(tabButton).toBeVisible()
await tabButton.click()
const essentialsTab = comfyPage.page.getByRole('tab', {
name: /essentials/i
})
await expect(essentialsTab).toBeVisible()
await essentialsTab.click()
const essentialCards = comfyPage.page.locator('[data-node-name]')
@@ -72,11 +76,13 @@ test.describe('Node Library Essentials Tab', { tag: '@ui' }, () => {
test('Essential node cards have node names', async ({ comfyPage }) => {
const tabButton = comfyPage.page.locator('.node-library-tab-button')
await expect(tabButton).toBeVisible()
await tabButton.click()
const essentialsTab = comfyPage.page.getByRole('tab', {
name: /essentials/i
})
await expect(essentialsTab).toBeVisible()
await essentialsTab.click()
const firstCard = comfyPage.page.locator('[data-node-name]').first()
@@ -91,6 +97,7 @@ test.describe('Node Library Essentials Tab', { tag: '@ui' }, () => {
comfyPage
}) => {
const tabButton = comfyPage.page.locator('.node-library-tab-button')
await expect(tabButton).toBeVisible()
await tabButton.click()
const essentialsTab = comfyPage.page.getByRole('tab', {
@@ -98,11 +105,13 @@ test.describe('Node Library Essentials Tab', { tag: '@ui' }, () => {
})
const allNodesTab = comfyPage.page.getByRole('tab', { name: /^all$/i })
await expect(essentialsTab).toBeVisible()
await essentialsTab.click()
await expect(essentialsTab).toHaveAttribute('aria-selected', 'true')
const essentialCards = comfyPage.page.locator('[data-node-name]')
await expect(essentialCards.first()).toBeVisible()
await expect(allNodesTab).toBeVisible()
await allNodesTab.click()
await expect(allNodesTab).toHaveAttribute('aria-selected', 'true')
await expect(essentialsTab).toHaveAttribute('aria-selected', 'false')

View File

@@ -127,6 +127,7 @@ test.describe('Node search box', { tag: '@node' }, () => {
const initialNodeCount = await comfyPage.nodeOps.getGraphNodesCount()
await comfyPage.canvasOps.disconnectEdge()
await expect(comfyPage.searchBox.input).toHaveCount(1)
await expect(comfyPage.page.locator('.p-chip-remove-icon')).toBeVisible()
await comfyPage.page.locator('.p-chip-remove-icon').click()
await comfyPage.searchBox.fillAndSelectFirstNode('KSampler', {
exact: true
@@ -197,6 +198,7 @@ test.describe('Node search box', { tag: '@node' }, () => {
test('Outer click dismisses filter panel but keeps search box visible', async ({
comfyPage
}) => {
await expect(comfyPage.searchBox.filterButton).toBeVisible()
await comfyPage.searchBox.filterButton.click()
const panel = comfyPage.searchBox.filterSelectionPanel
await panel.header.waitFor({ state: 'visible' })

View File

@@ -64,6 +64,7 @@ test.describe('Node search box V2', { tag: '@node' }, () => {
await comfyPage.canvasOps.doubleClick()
await expect(searchBoxV2.input).toBeVisible()
await expect(searchBoxV2.categoryButton('favorites')).toBeVisible()
await searchBoxV2.categoryButton('favorites').click()
await expect(searchBoxV2.results).toHaveCount(1)
@@ -78,6 +79,7 @@ test.describe('Node search box V2', { tag: '@node' }, () => {
await comfyPage.canvasOps.doubleClick()
await expect(searchBoxV2.input).toBeVisible()
await expect(searchBoxV2.categoryButton('sampling')).toBeVisible()
await searchBoxV2.categoryButton('sampling').click()
await expect(searchBoxV2.results.first()).toBeVisible()
@@ -94,6 +96,7 @@ test.describe('Node search box V2', { tag: '@node' }, () => {
await expect(searchBoxV2.input).toBeVisible()
// Click "Input" filter chip in the filter bar
await expect(searchBoxV2.filterBarButton('Input')).toBeVisible()
await searchBoxV2.filterBarButton('Input').click()
// Filter options should appear

View File

@@ -69,10 +69,12 @@ test.describe('Node search box V2 extended', { tag: '@node' }, () => {
await comfyPage.canvasOps.doubleClick()
await expect(searchBoxV2.input).toBeVisible()
await expect(searchBoxV2.categoryButton('sampling')).toBeVisible()
await searchBoxV2.categoryButton('sampling').click()
await expect(searchBoxV2.results.first()).toBeVisible()
const samplingResults = await searchBoxV2.results.allTextContents()
await expect(searchBoxV2.categoryButton('loaders')).toBeVisible()
await searchBoxV2.categoryButton('loaders').click()
await expect(searchBoxV2.results.first()).toBeVisible()
const loaderResults = await searchBoxV2.results.allTextContents()
@@ -93,6 +95,7 @@ test.describe('Node search box V2 extended', { tag: '@node' }, () => {
const unfilteredResults = await searchBoxV2.results.allTextContents()
// Apply Input filter with MODEL type
await expect(searchBoxV2.filterBarButton('Input')).toBeVisible()
await searchBoxV2.filterBarButton('Input').click()
await expect(searchBoxV2.filterOptions.first()).toBeVisible()
await searchBoxV2.input.fill('MODEL')
@@ -111,6 +114,7 @@ test.describe('Node search box V2 extended', { tag: '@node' }, () => {
expect(filteredResults).not.toEqual(unfilteredResults)
// Remove filter by clicking the chip delete button
await expect(filterChip.getByTestId('chip-delete')).toBeVisible()
await filterChip.getByTestId('chip-delete').click()
// Filter chip should be removed

View File

@@ -19,6 +19,7 @@ test.describe('Paste Image context menu option', { tag: ['@node'] }, () => {
const nodeEl = comfyPage.page.locator(
`[data-node-id="${loadImageNode.id}"]`
)
await expect(nodeEl).toBeVisible()
await nodeEl.click({ button: 'right' })
const menu = comfyPage.page.locator('.p-contextmenu')
await menu.waitFor({ state: 'visible' })
@@ -41,6 +42,7 @@ test.describe('Paste Image context menu option', { tag: ['@node'] }, () => {
const nodeEl = comfyPage.page.locator(
`[data-node-id="${saveImageNode.id}"]`
)
await expect(nodeEl).toBeVisible()
await nodeEl.click({ button: 'right' })
const menu = comfyPage.page.locator('.p-contextmenu')
await menu.waitFor({ state: 'visible' })

View File

@@ -12,6 +12,9 @@ export async function openErrorsTabViaSeeErrors(
const errorOverlay = comfyPage.page.getByTestId(TestIds.dialogs.errorOverlay)
await expect(errorOverlay).toBeVisible()
await expect(
errorOverlay.getByTestId(TestIds.dialogs.errorOverlaySeeErrors)
).toBeVisible()
await errorOverlay.getByTestId(TestIds.dialogs.errorOverlaySeeErrors).click()
await expect(errorOverlay).not.toBeVisible()
}

View File

@@ -68,6 +68,7 @@ export class PropertiesPanelHelper {
async open(actionbar: Locator): Promise<void> {
if (!(await this.root.isVisible())) {
await expect(actionbar).toBeVisible()
await actionbar.click()
await expect(this.root).toBeVisible()
}
@@ -75,16 +76,19 @@ export class PropertiesPanelHelper {
async close(): Promise<void> {
if (await this.root.isVisible()) {
await expect(this.closeButton).toBeVisible()
await this.closeButton.click()
await expect(this.root).not.toBeVisible()
}
}
async switchToTab(label: string): Promise<void> {
await expect(this.getTab(label)).toBeVisible()
await this.getTab(label).click()
}
async editTitle(newTitle: string): Promise<void> {
await expect(this.titleEditIcon).toBeVisible()
await this.titleEditIcon.click()
await this.titleInput.fill(newTitle)
await this.titleInput.press('Enter')

View File

@@ -16,6 +16,7 @@ test.describe('Errors tab - common', { tag: '@ui' }, () => {
test.describe('Tab visibility', () => {
test('Should show Errors tab when errors exist', async ({ comfyPage }) => {
await comfyPage.workflow.loadWorkflow('missing/missing_nodes')
await expect(comfyPage.actionbar.propertiesButton).toBeVisible()
await comfyPage.actionbar.propertiesButton.click()
await comfyPage.nextFrame()
@@ -31,6 +32,7 @@ test.describe('Errors tab - common', { tag: '@ui' }, () => {
'Comfy.RightSidePanel.ShowErrorsTab',
false
)
await expect(comfyPage.actionbar.propertiesButton).toBeVisible()
await comfyPage.actionbar.propertiesButton.click()
await comfyPage.nextFrame()

View File

@@ -21,6 +21,7 @@ async function confirmPendingSelection(comfyPage: ComfyPage) {
TestIds.dialogs.missingMediaConfirmButton
)
await expect(confirmButton).toBeEnabled()
await expect(confirmButton).toBeVisible()
await confirmButton.click()
}
@@ -100,6 +101,7 @@ test.describe('Errors tab - Missing media', { tag: '@ui' }, () => {
const librarySelect = comfyPage.page.getByTestId(
TestIds.dialogs.missingMediaLibrarySelect
)
await expect(librarySelect.getByRole('combobox')).toBeVisible()
await librarySelect.getByRole('combobox').click()
const optionCount = await comfyPage.page.getByRole('option').count()
@@ -108,6 +110,7 @@ test.describe('Errors tab - Missing media', { tag: '@ui' }, () => {
return
}
await expect(comfyPage.page.getByRole('option').first()).toBeVisible()
await comfyPage.page.getByRole('option').first().click()
await expect(getStatusCard(comfyPage)).toBeVisible()

View File

@@ -60,6 +60,7 @@ test.describe('Errors tab - Missing models', { tag: '@ui' }, () => {
TestIds.dialogs.missingModelExpand
)
await expect(expandButton.first()).toBeVisible()
await expect(expandButton.first()).toBeVisible()
await expandButton.first().click()
await expect(locateButton.first()).toBeVisible()
@@ -73,6 +74,7 @@ test.describe('Errors tab - Missing models', { tag: '@ui' }, () => {
TestIds.dialogs.missingModelCopyName
)
await expect(copyButton.first()).toBeVisible()
await expect(copyButton.first()).toBeVisible()
await copyButton.first().click()
const copiedText = await getClipboardText(comfyPage.page)

View File

@@ -8,6 +8,7 @@ test.describe('Properties panel - Info tab', () => {
test.beforeEach(async ({ comfyPage }) => {
panel = new PropertiesPanelHelper(comfyPage.page)
await expect(comfyPage.actionbar.propertiesButton).toBeVisible()
await comfyPage.actionbar.propertiesButton.click()
await comfyPage.nodeOps.selectNodes(['KSampler'])
await panel.switchToTab('Info')

View File

@@ -8,6 +8,7 @@ test.describe('Properties panel - Node selection', () => {
test.beforeEach(async ({ comfyPage }) => {
panel = new PropertiesPanelHelper(comfyPage.page)
await expect(comfyPage.actionbar.propertiesButton).toBeVisible()
await comfyPage.actionbar.propertiesButton.click()
})

View File

@@ -10,6 +10,7 @@ test.describe('Properties panel - Node settings', () => {
panel = new PropertiesPanelHelper(comfyPage.page)
await comfyPage.settings.setSetting('Comfy.VueNodes.Enabled', true)
await comfyPage.vueNodes.waitForNodes()
await expect(comfyPage.actionbar.propertiesButton).toBeVisible()
await comfyPage.actionbar.propertiesButton.click()
await comfyPage.nodeOps.selectNodes(['KSampler'])
await panel.switchToTab('Settings')
@@ -23,6 +24,7 @@ test.describe('Properties panel - Node settings', () => {
})
test('should set node to Bypass mode', async ({ comfyPage }) => {
await expect(panel.getNodeStateButton('Bypass')).toBeVisible()
await panel.getNodeStateButton('Bypass').click()
const nodeLocator = comfyPage.vueNodes.getNodeByTitle('KSampler')
@@ -30,6 +32,7 @@ test.describe('Properties panel - Node settings', () => {
})
test('should set node to Mute mode', async ({ comfyPage }) => {
await expect(panel.getNodeStateButton('Mute')).toBeVisible()
await panel.getNodeStateButton('Mute').click()
const nodeLocator = comfyPage.vueNodes.getNodeByTitle('KSampler')
@@ -37,10 +40,12 @@ test.describe('Properties panel - Node settings', () => {
})
test('should restore node to Normal mode', async ({ comfyPage }) => {
await expect(panel.getNodeStateButton('Bypass')).toBeVisible()
await panel.getNodeStateButton('Bypass').click()
const nodeLocator = comfyPage.vueNodes.getNodeByTitle('KSampler')
await expect(nodeLocator.getByText('Bypassed')).toBeVisible()
await expect(panel.getNodeStateButton('Normal')).toBeVisible()
await panel.getNodeStateButton('Normal').click()
await expect(nodeLocator.getByText('Bypassed')).not.toBeVisible()
await expect(nodeLocator.getByText('Muted')).not.toBeVisible()
@@ -55,6 +60,7 @@ test.describe('Properties panel - Node settings', () => {
})
test('should apply color to node', async ({ comfyPage }) => {
await expect(panel.getColorSwatch('red')).toBeVisible()
await panel.getColorSwatch('red').click()
await expect
@@ -69,6 +75,7 @@ test.describe('Properties panel - Node settings', () => {
})
test('should remove color with noColor swatch', async ({ comfyPage }) => {
await expect(panel.getColorSwatch('red')).toBeVisible()
await panel.getColorSwatch('red').click()
await expect
@@ -81,6 +88,7 @@ test.describe('Properties panel - Node settings', () => {
)
.toBe(true)
await expect(panel.getColorSwatch('noColor')).toBeVisible()
await panel.getColorSwatch('noColor').click()
await expect
@@ -110,6 +118,7 @@ test.describe('Properties panel - Node settings', () => {
test('should unpin previously pinned node', async ({ comfyPage }) => {
const nodeLocator = comfyPage.vueNodes.getNodeByTitle('KSampler')
await expect(panel.pinnedSwitch).toBeVisible()
await panel.pinnedSwitch.click()
await expect(nodeLocator.getByTestId('node-pin-indicator')).toBeVisible()

View File

@@ -12,6 +12,7 @@ test.describe('Properties panel - Open and close', () => {
test('should open via actionbar toggle button', async ({ comfyPage }) => {
await expect(panel.root).not.toBeVisible()
await expect(comfyPage.actionbar.propertiesButton).toBeVisible()
await comfyPage.actionbar.propertiesButton.click()
await expect(panel.root).toBeVisible()
})
@@ -19,11 +20,13 @@ test.describe('Properties panel - Open and close', () => {
test('should close via panel close button', async ({ comfyPage }) => {
await comfyPage.actionbar.propertiesButton.click()
await expect(panel.root).toBeVisible()
await expect(panel.closeButton).toBeVisible()
await panel.closeButton.click()
await expect(panel.root).not.toBeVisible()
})
test('should close via close button after opening', async ({ comfyPage }) => {
await expect(comfyPage.actionbar.propertiesButton).toBeVisible()
await comfyPage.actionbar.propertiesButton.click()
await expect(panel.root).toBeVisible()
await panel.close()

View File

@@ -8,6 +8,7 @@ test.describe('Properties panel position', () => {
await comfyPage.settings.setSetting('Comfy.NodeLibrary.NewDesign', false)
// Open a sidebar tab to ensure sidebar is visible
await comfyPage.menu.nodeLibraryTab.open()
await expect(comfyPage.actionbar.propertiesButton).toBeVisible()
await comfyPage.actionbar.propertiesButton.click()
})

View File

@@ -8,6 +8,7 @@ test.describe('Properties panel - Search filtering', () => {
test.beforeEach(async ({ comfyPage }) => {
panel = new PropertiesPanelHelper(comfyPage.page)
await expect(comfyPage.actionbar.propertiesButton).toBeVisible()
await comfyPage.actionbar.propertiesButton.click()
await comfyPage.nodeOps.selectNodes([
'KSampler',

View File

@@ -8,6 +8,7 @@ test.describe('Properties panel - Title editing', () => {
test.beforeEach(async ({ comfyPage }) => {
panel = new PropertiesPanelHelper(comfyPage.page)
await expect(comfyPage.actionbar.propertiesButton).toBeVisible()
await comfyPage.actionbar.propertiesButton.click()
await comfyPage.nodeOps.selectNodes(['KSampler'])
})

View File

@@ -8,6 +8,7 @@ test.describe('Properties panel - Workflow Overview', () => {
test.beforeEach(async ({ comfyPage }) => {
panel = new PropertiesPanelHelper(comfyPage.page)
await expect(comfyPage.actionbar.propertiesButton).toBeVisible()
await comfyPage.actionbar.propertiesButton.click()
await expect(panel.root).toBeVisible()
})

View File

@@ -47,6 +47,7 @@ test.describe('Queue overlay', () => {
test('Toggle button opens expanded queue overlay', async ({ comfyPage }) => {
const toggle = comfyPage.page.getByTestId(TestIds.queue.overlayToggle)
await expect(toggle).toBeVisible()
await toggle.click()
// Expanded overlay should show job items
@@ -55,6 +56,7 @@ test.describe('Queue overlay', () => {
test('Overlay shows filter tabs (All, Completed)', async ({ comfyPage }) => {
const toggle = comfyPage.page.getByTestId(TestIds.queue.overlayToggle)
await expect(toggle).toBeVisible()
await toggle.click()
await expect(
@@ -69,6 +71,7 @@ test.describe('Queue overlay', () => {
comfyPage
}) => {
const toggle = comfyPage.page.getByTestId(TestIds.queue.overlayToggle)
await expect(toggle).toBeVisible()
await toggle.click()
await expect(comfyPage.page.locator('[data-job-id]').first()).toBeVisible()
@@ -80,6 +83,7 @@ test.describe('Queue overlay', () => {
test('Completed filter shows only completed jobs', async ({ comfyPage }) => {
const toggle = comfyPage.page.getByTestId(TestIds.queue.overlayToggle)
await expect(toggle).toBeVisible()
await toggle.click()
await expect(comfyPage.page.locator('[data-job-id]').first()).toBeVisible()
@@ -98,6 +102,7 @@ test.describe('Queue overlay', () => {
test('Toggling overlay again closes it', async ({ comfyPage }) => {
const toggle = comfyPage.page.getByTestId(TestIds.queue.overlayToggle)
await expect(toggle).toBeVisible()
await toggle.click()
await expect(comfyPage.page.locator('[data-job-id]').first()).toBeVisible()

View File

@@ -334,6 +334,7 @@ test.describe('Release Notifications', () => {
)
// Reopen help center
await expect(helpCenterButton).toBeVisible()
await helpCenterButton.click()
// Verify "What's New?" section is now hidden

View File

@@ -13,9 +13,11 @@ test.describe('Remote COMBO Widget', { tag: '@widget' }, () => {
) => {
const tab = comfyPage.menu.nodeLibraryTab
await tab.open()
await expect(tab.getFolder('DevTools')).toBeVisible()
await tab.getFolder('DevTools').click()
const nodeEntry = tab.getNode(nodeName).first()
for (let i = 0; i < count; i++) {
await expect(nodeEntry).toBeVisible()
await nodeEntry.click()
await comfyPage.nextFrame()
}

View File

@@ -23,13 +23,16 @@ test.describe('Reroute Node', { tag: ['@screenshot', '@node'] }, () => {
// Insert the workflow
const workflowsTab = comfyPage.menu.workflowsTab
await workflowsTab.open()
await expect(workflowsTab.getPersistedItem(workflowName)).toBeVisible()
await workflowsTab.getPersistedItem(workflowName).click({ button: 'right' })
const insertButton = comfyPage.page.locator('.p-contextmenu-item-link', {
hasText: 'Insert'
})
await expect(insertButton).toBeVisible()
await insertButton.click()
// Close the sidebar tab
await expect(workflowsTab.tabButton).toBeVisible()
await workflowsTab.tabButton.click()
await workflowsTab.root.waitFor({ state: 'hidden' })
await comfyPage.setFocusMode(true)

View File

@@ -15,6 +15,7 @@ test.describe('MediaLightbox', { tag: ['@slow'] }, () => {
'widgets/save_image_and_animated_webp'
)
await comfyPage.vueNodes.waitForNodes()
await expect(comfyPage.runButton).toBeVisible()
await comfyPage.runButton.click()
// Wait for SaveImage node to produce output
@@ -24,6 +25,7 @@ test.describe('MediaLightbox', { tag: ['@slow'] }, () => {
})
// Open Assets sidebar tab and wait for it to load
await expect(comfyPage.page.locator('.assets-tab-button')).toBeVisible()
await comfyPage.page.locator('.assets-tab-button').click()
await comfyPage.page
.locator('.sidebar-content-container')
@@ -39,6 +41,7 @@ test.describe('MediaLightbox', { tag: ['@slow'] }, () => {
// Hover to reveal zoom button, then click it
await assetCard.hover()
await expect(assetCard.getByLabel('Zoom in')).toBeVisible()
await assetCard.getByLabel('Zoom in').click()
const { root } = comfyPage.mediaLightbox
@@ -62,6 +65,7 @@ test.describe('MediaLightbox', { tag: ['@slow'] }, () => {
test('closes gallery when clicking close button', async ({ comfyPage }) => {
await runAndOpenGallery(comfyPage)
await expect(comfyPage.mediaLightbox.closeButton).toBeVisible()
await comfyPage.mediaLightbox.closeButton.click()
await expect(comfyPage.mediaLightbox.root).not.toBeVisible()
})

View File

@@ -15,10 +15,13 @@ test.describe(
test('Can add node', async ({ comfyPage }) => {
await comfyPage.canvasOps.rightClick()
await expect(comfyPage.canvas).toHaveScreenshot('right-click-menu.png')
await expect(comfyPage.page.getByText('Add Node')).toBeVisible()
await comfyPage.page.getByText('Add Node').click()
await comfyPage.nextFrame()
await expect(comfyPage.page.getByText('loaders')).toBeVisible()
await comfyPage.page.getByText('loaders').click()
await comfyPage.nextFrame()
await expect(comfyPage.page.getByText('Load VAE')).toBeVisible()
await comfyPage.page.getByText('Load VAE').click()
await comfyPage.nextFrame()
await expect(comfyPage.canvas).toHaveScreenshot('add-node-node-added.png')
@@ -27,6 +30,9 @@ test.describe(
test('Can add group', async ({ comfyPage }) => {
await comfyPage.canvasOps.rightClick()
await expect(comfyPage.canvas).toHaveScreenshot('right-click-menu.png')
await expect(
comfyPage.page.getByText('Add Group', { exact: true })
).toBeVisible()
await comfyPage.page.getByText('Add Group', { exact: true }).click()
await comfyPage.nextFrame()
await expect(comfyPage.canvas).toHaveScreenshot(
@@ -62,6 +68,7 @@ test.describe('Node Right Click Menu', { tag: ['@screenshot', '@ui'] }, () => {
await comfyPage.page.mouse.move(10, 10)
await comfyPage.nextFrame()
await expect(comfyPage.canvas).toHaveScreenshot('right-click-node.png')
await expect(comfyPage.page.getByText('Properties Panel')).toBeVisible()
await comfyPage.page.getByText('Properties Panel').click()
await comfyPage.nextFrame()
await expect(comfyPage.canvas).toHaveScreenshot(
@@ -77,6 +84,7 @@ test.describe('Node Right Click Menu', { tag: ['@screenshot', '@ui'] }, () => {
await comfyPage.page.mouse.move(10, 10)
await comfyPage.nextFrame()
await expect(comfyPage.canvas).toHaveScreenshot('right-click-node.png')
await expect(comfyPage.page.getByText('Collapse')).toBeVisible()
await comfyPage.page.getByText('Collapse').click()
await comfyPage.nextFrame()
await expect(comfyPage.canvas).toHaveScreenshot(
@@ -100,6 +108,7 @@ test.describe('Node Right Click Menu', { tag: ['@screenshot', '@ui'] }, () => {
})
await comfyPage.page.mouse.move(10, 10)
await comfyPage.nextFrame()
await expect(comfyPage.page.getByText('Collapse')).toBeVisible()
await comfyPage.page.getByText('Collapse').click()
await comfyPage.nextFrame()
await expect(comfyPage.canvas).toHaveScreenshot(
@@ -115,6 +124,7 @@ test.describe('Node Right Click Menu', { tag: ['@screenshot', '@ui'] }, () => {
await comfyPage.page.mouse.move(10, 10)
await comfyPage.nextFrame()
await expect(comfyPage.canvas).toHaveScreenshot('right-click-node.png')
await expect(comfyPage.page.getByText('Bypass')).toBeVisible()
await comfyPage.page.getByText('Bypass').click()
await comfyPage.nextFrame()
await expect(comfyPage.canvas).toHaveScreenshot(
@@ -226,6 +236,7 @@ test.describe('Node Right Click Menu', { tag: ['@screenshot', '@ui'] }, () => {
const cloneItem = comfyPage.page.locator(
'.litemenu-entry:has-text("Clone")'
)
await expect(cloneItem).toBeVisible()
await cloneItem.click()
await expect(cloneItem).toHaveCount(0)
await comfyPage.nextFrame()

View File

@@ -12,6 +12,7 @@ test.describe('Right Side Panel Tabs', { tag: '@ui' }, () => {
test('Properties panel opens with workflow overview', async ({
comfyPage
}) => {
await expect(comfyPage.actionbar.propertiesButton).toBeVisible()
await comfyPage.actionbar.propertiesButton.click()
const { propertiesPanel } = comfyPage.menu
@@ -22,6 +23,7 @@ test.describe('Right Side Panel Tabs', { tag: '@ui' }, () => {
test('Properties panel shows node details on selection', async ({
comfyPage
}) => {
await expect(comfyPage.actionbar.propertiesButton).toBeVisible()
await comfyPage.actionbar.propertiesButton.click()
const { propertiesPanel } = comfyPage.menu
@@ -31,6 +33,7 @@ test.describe('Right Side Panel Tabs', { tag: '@ui' }, () => {
})
test('Node title input is editable', async ({ comfyPage }) => {
await expect(comfyPage.actionbar.propertiesButton).toBeVisible()
await comfyPage.actionbar.propertiesButton.click()
const { propertiesPanel } = comfyPage.menu
@@ -38,6 +41,7 @@ test.describe('Right Side Panel Tabs', { tag: '@ui' }, () => {
await expect(propertiesPanel.panelTitle).toContainText('KSampler')
// Click on the title to enter edit mode
await expect(propertiesPanel.panelTitle).toBeVisible()
await propertiesPanel.panelTitle.click()
const titleInput = propertiesPanel.root.getByTestId(TestIds.node.titleInput)
await expect(titleInput).toBeVisible()
@@ -49,6 +53,7 @@ test.describe('Right Side Panel Tabs', { tag: '@ui' }, () => {
})
test('Search box filters properties', async ({ comfyPage }) => {
await expect(comfyPage.actionbar.propertiesButton).toBeVisible()
await comfyPage.actionbar.propertiesButton.click()
const { propertiesPanel } = comfyPage.menu
@@ -77,6 +82,7 @@ test.describe('Right Side Panel Tabs', { tag: '@ui' }, () => {
})
test('Expand all / Collapse all toggles sections', async ({ comfyPage }) => {
await expect(comfyPage.actionbar.propertiesButton).toBeVisible()
await comfyPage.actionbar.propertiesButton.click()
const { propertiesPanel } = comfyPage.menu
@@ -105,6 +111,7 @@ test.describe('Right Side Panel Tabs', { tag: '@ui' }, () => {
})
test('Properties panel can be closed', async ({ comfyPage }) => {
await expect(comfyPage.actionbar.propertiesButton).toBeVisible()
await comfyPage.actionbar.propertiesButton.click()
const { propertiesPanel } = comfyPage.menu
@@ -113,6 +120,7 @@ test.describe('Right Side Panel Tabs', { tag: '@ui' }, () => {
// The actionbar toggle button hides when the panel is open,
// so use the close button inside the panel itself
const closeButton = comfyPage.page.getByLabel('Toggle properties panel')
await expect(closeButton).toBeVisible()
await closeButton.click()
await expect(propertiesPanel.root).toBeHidden()
})

View File

@@ -19,6 +19,7 @@ test.describe(
)
await comfyPage.vueNodes.waitForNodes()
await expect(comfyPage.runButton).toBeVisible()
await comfyPage.runButton.click()
const saveImageNode = comfyPage.vueNodes.getNodeByTitle('Save Image')

View File

@@ -45,6 +45,7 @@ test.describe('@canvas Selection Rectangle', () => {
})
test('Single click selects one node', async ({ comfyPage }) => {
await expect(comfyPage.page.getByText('Load Checkpoint')).toBeVisible()
await comfyPage.page.getByText('Load Checkpoint').click()
await comfyPage.nextFrame()
@@ -52,10 +53,12 @@ test.describe('@canvas Selection Rectangle', () => {
})
test('Ctrl+click adds to selection', async ({ comfyPage }) => {
await expect(comfyPage.page.getByText('Load Checkpoint')).toBeVisible()
await comfyPage.page.getByText('Load Checkpoint').click()
await comfyPage.nextFrame()
await expect.poll(() => comfyPage.vueNodes.getSelectedNodeCount()).toBe(1)
await expect(comfyPage.page.getByText('Empty Latent Image')).toBeVisible()
await comfyPage.page.getByText('Empty Latent Image').click({
modifiers: ['Control']
})
@@ -66,6 +69,7 @@ test.describe('@canvas Selection Rectangle', () => {
test('Selected nodes have visual indicator', async ({ comfyPage }) => {
const checkpointNode = comfyPage.vueNodes.getNodeByTitle('Load Checkpoint')
await expect(comfyPage.page.getByText('Load Checkpoint')).toBeVisible()
await comfyPage.page.getByText('Load Checkpoint').click()
await comfyPage.nextFrame()

View File

@@ -159,6 +159,7 @@ test.describe('Selection Toolbox', { tag: ['@screenshot', '@ui'] }, () => {
const blueColorOption = colorPickerGroup.getByTestId(
TestIds.selectionToolbox.colorBlue
)
await expect(blueColorOption).toBeVisible()
await blueColorOption.click()
// Dropdown should close after selection
@@ -188,10 +189,12 @@ test.describe('Selection Toolbox', { tag: ['@screenshot', '@ui'] }, () => {
await expect(colorPickerButton).not.toHaveAttribute('color')
// Click color picker and select a color
await expect(colorPickerButton).toBeVisible()
await colorPickerButton.click()
const redColorOption = getColorPickerGroup(comfyPage).getByTestId(
TestIds.selectionToolbox.colorRed
)
await expect(redColorOption).toBeVisible()
await redColorOption.click()
// Button should now show the selected color
@@ -203,6 +206,7 @@ test.describe('Selection Toolbox', { tag: ['@screenshot', '@ui'] }, () => {
}) => {
// Select first node and color it
await comfyPage.nodeOps.selectNodes(['KSampler'])
await expect(getColorPickerButton(comfyPage)).toBeVisible()
await getColorPickerButton(comfyPage).click()
await getColorPickerGroup(comfyPage)
.getByTestId(TestIds.selectionToolbox.colorBlue)
@@ -211,6 +215,7 @@ test.describe('Selection Toolbox', { tag: ['@screenshot', '@ui'] }, () => {
// Select second node and color it differently
await comfyPage.nodeOps.selectNodes(['CLIP Text Encode (Prompt)'])
await expect(getColorPickerButton(comfyPage)).toBeVisible()
await getColorPickerButton(comfyPage).click()
await getColorPickerGroup(comfyPage)
.getByTestId(TestIds.selectionToolbox.colorRed)
@@ -232,6 +237,7 @@ test.describe('Selection Toolbox', { tag: ['@screenshot', '@ui'] }, () => {
}) => {
// First color a node
await comfyPage.nodeOps.selectNodes(['KSampler'])
await expect(getColorPickerButton(comfyPage)).toBeVisible()
await getColorPickerButton(comfyPage).click()
await getColorPickerGroup(comfyPage)
.getByTestId(TestIds.selectionToolbox.colorBlue)
@@ -253,6 +259,7 @@ test.describe('Selection Toolbox', { tag: ['@screenshot', '@ui'] }, () => {
}) => {
// Select a node and color it
await comfyPage.nodeOps.selectNodes(['KSampler'])
await expect(getColorPickerButton(comfyPage)).toBeVisible()
await getColorPickerButton(comfyPage).click()
await getColorPickerGroup(comfyPage)
.getByTestId(TestIds.selectionToolbox.colorBlue)

View File

@@ -103,6 +103,9 @@ test.describe(
).toBeVisible({
timeout: 5000
})
await expect(
comfyPage.page.getByText('Box', { exact: true })
).toBeVisible()
await comfyPage.page.getByText('Box', { exact: true }).click()
await comfyPage.nextFrame()
@@ -122,9 +125,13 @@ test.describe(
)
await openMoreOptions(comfyPage)
await expect(
comfyPage.page.getByText('Color', { exact: true })
).toBeVisible()
await comfyPage.page.getByText('Color', { exact: true }).click()
const blueSwatch = comfyPage.page.locator('[title="Blue"]')
await expect(blueSwatch.first()).toBeVisible({ timeout: 5000 })
await expect(blueSwatch.first()).toBeVisible()
await blueSwatch.first().click()
await comfyPage.nextFrame()

View File

@@ -241,6 +241,7 @@ test.describe('Assets sidebar - view mode toggle', () => {
// Open settings menu and select list view
await tab.openSettingsMenu()
await expect(tab.listViewOption).toBeVisible()
await tab.listViewOption.click()
// List view items should now be visible
@@ -254,10 +255,12 @@ test.describe('Assets sidebar - view mode toggle', () => {
// Switch to list view
await tab.openSettingsMenu()
await expect(tab.listViewOption).toBeVisible()
await tab.listViewOption.click()
await expect(tab.listViewItems.first()).toBeVisible({ timeout: 5000 })
// Switch back to grid view (settings popover is still open)
await expect(tab.gridViewOption).toBeVisible()
await tab.gridViewOption.click()
await tab.waitForAssets()
@@ -355,6 +358,7 @@ test.describe('Assets sidebar - selection', () => {
await tab.waitForAssets()
// Click first asset card
await expect(tab.assetCards.first()).toBeVisible()
await tab.assetCards.first().click()
// Should have data-selected="true"
@@ -371,10 +375,12 @@ test.describe('Assets sidebar - selection', () => {
expect(cardCount).toBeGreaterThanOrEqual(2)
// Click first card
await expect(cards.first()).toBeVisible()
await cards.first().click()
await expect(tab.selectedCards).toHaveCount(1)
// Ctrl+click second card
await expect(cards.nth(1)).toBeVisible()
await cards.nth(1).click({ modifiers: ['ControlOrMeta'] })
await expect(tab.selectedCards).toHaveCount(2)
})
@@ -387,6 +393,7 @@ test.describe('Assets sidebar - selection', () => {
await tab.waitForAssets()
// Select an asset
await expect(tab.assetCards.first()).toBeVisible()
await tab.assetCards.first().click()
// Footer should show selection count
@@ -399,6 +406,7 @@ test.describe('Assets sidebar - selection', () => {
await tab.waitForAssets()
// Select an asset
await expect(tab.assetCards.first()).toBeVisible()
await tab.assetCards.first().click()
await expect(tab.selectedCards).toHaveCount(1)
@@ -417,6 +425,7 @@ test.describe('Assets sidebar - selection', () => {
await tab.waitForAssets()
// Select an asset
await expect(tab.assetCards.first()).toBeVisible()
await tab.assetCards.first().click()
await expect(tab.selectedCards).toHaveCount(1)
@@ -451,6 +460,7 @@ test.describe('Assets sidebar - context menu', () => {
await tab.waitForAssets()
// Right-click first asset
await expect(tab.assetCards.first()).toBeVisible()
await tab.assetCards.first().click({ button: 'right' })
// Context menu should appear with standard items
@@ -465,6 +475,7 @@ test.describe('Assets sidebar - context menu', () => {
await tab.open()
await tab.waitForAssets()
await expect(tab.assetCards.first()).toBeVisible()
await tab.assetCards.first().click({ button: 'right' })
await comfyPage.page
.locator('.p-contextmenu')
@@ -480,6 +491,7 @@ test.describe('Assets sidebar - context menu', () => {
await tab.open()
await tab.waitForAssets()
await expect(tab.assetCards.first()).toBeVisible()
await tab.assetCards.first().click({ button: 'right' })
await comfyPage.page
.locator('.p-contextmenu')
@@ -495,6 +507,7 @@ test.describe('Assets sidebar - context menu', () => {
await tab.open()
await tab.waitForAssets()
await expect(tab.assetCards.first()).toBeVisible()
await tab.assetCards.first().click({ button: 'right' })
await comfyPage.page
.locator('.p-contextmenu')
@@ -510,6 +523,7 @@ test.describe('Assets sidebar - context menu', () => {
await tab.open()
await tab.waitForAssets()
await expect(tab.assetCards.first()).toBeVisible()
await tab.assetCards.first().click({ button: 'right' })
await comfyPage.page
.locator('.p-contextmenu')
@@ -525,6 +539,7 @@ test.describe('Assets sidebar - context menu', () => {
await tab.open()
await tab.waitForAssets()
await expect(tab.assetCards.first()).toBeVisible()
await tab.assetCards.first().click({ button: 'right' })
const contextMenu = comfyPage.page.locator('.p-contextmenu')
@@ -553,8 +568,10 @@ test.describe('Assets sidebar - context menu', () => {
// Multi-select: use keyboard.down/up so useKeyModifier('Control') detects
// the modifier — click({ modifiers }) only sets the mouse event flag and
// does not fire a keydown event that VueUse tracks.
await expect(cards.first()).toBeVisible()
await cards.first().click()
await comfyPage.page.keyboard.down('Control')
await expect(cards.nth(1)).toBeVisible()
await cards.nth(1).click()
await comfyPage.page.keyboard.up('Control')
@@ -599,6 +616,7 @@ test.describe('Assets sidebar - bulk actions', () => {
await tab.open()
await tab.waitForAssets()
await expect(tab.assetCards.first()).toBeVisible()
await tab.assetCards.first().click()
// Download button in footer should be visible
@@ -612,6 +630,7 @@ test.describe('Assets sidebar - bulk actions', () => {
await tab.open()
await tab.waitForAssets()
await expect(tab.assetCards.first()).toBeVisible()
await tab.assetCards.first().click()
// Delete button in footer should be visible
@@ -628,7 +647,9 @@ test.describe('Assets sidebar - bulk actions', () => {
const cardCount = await cards.count()
expect(cardCount).toBeGreaterThanOrEqual(2)
await expect(cards.first()).toBeVisible()
await cards.first().click()
await expect(cards.nth(1)).toBeVisible()
await cards.nth(1).click({ modifiers: ['ControlOrMeta'] })
// Selection count should show the count
@@ -723,7 +744,9 @@ test.describe('Assets sidebar - delete confirmation', () => {
await tab.open()
await tab.waitForAssets()
await expect(tab.assetCards.first()).toBeVisible()
await tab.assetCards.first().click({ button: 'right' })
await expect(tab.contextMenuItem('Delete')).toBeVisible()
await tab.contextMenuItem('Delete').click()
const dialog = comfyPage.confirmDialog.root
@@ -743,12 +766,15 @@ test.describe('Assets sidebar - delete confirmation', () => {
const initialCount = await tab.assetCards.count()
await expect(tab.assetCards.first()).toBeVisible()
await tab.assetCards.first().click({ button: 'right' })
await expect(tab.contextMenuItem('Delete')).toBeVisible()
await tab.contextMenuItem('Delete').click()
const dialog = comfyPage.confirmDialog.root
await expect(dialog).toBeVisible()
await expect(comfyPage.confirmDialog.delete).toBeVisible()
await comfyPage.confirmDialog.delete.click()
await expect(dialog).not.toBeVisible()
@@ -767,12 +793,15 @@ test.describe('Assets sidebar - delete confirmation', () => {
const initialCount = await tab.assetCards.count()
await expect(tab.assetCards.first()).toBeVisible()
await tab.assetCards.first().click({ button: 'right' })
await expect(tab.contextMenuItem('Delete')).toBeVisible()
await tab.contextMenuItem('Delete').click()
const dialog = comfyPage.confirmDialog.root
await expect(dialog).toBeVisible()
await expect(comfyPage.confirmDialog.reject).toBeVisible()
await comfyPage.confirmDialog.reject.click()
await expect(dialog).not.toBeVisible()

View File

@@ -73,6 +73,7 @@ test.describe('Model library sidebar - folders', () => {
await tab.open()
// Click the folder to expand it
await expect(tab.getFolderByLabel('checkpoints')).toBeVisible()
await tab.getFolderByLabel('checkpoints').click()
// Models should appear as leaf nodes
@@ -89,6 +90,7 @@ test.describe('Model library sidebar - folders', () => {
const tab = comfyPage.menu.modelLibraryTab
await tab.open()
await expect(tab.getFolderByLabel('loras')).toBeVisible()
await tab.getFolderByLabel('loras').click()
await expect(tab.getLeafByLabel('detail_tweaker_xl')).toBeVisible({
@@ -191,6 +193,7 @@ test.describe('Model library sidebar - refresh', () => {
(req) => req.url().endsWith('/experiment/models'),
{ timeout: 5000 }
)
await expect(tab.refreshButton).toBeVisible()
await tab.refreshButton.click()
await refreshRequest
@@ -214,6 +217,7 @@ test.describe('Model library sidebar - refresh', () => {
{ timeout: 5000 }
)
await expect(tab.loadAllFoldersButton).toBeVisible()
await tab.loadAllFoldersButton.click()
await folderRequest
})

View File

@@ -54,6 +54,7 @@ test.describe('Node library sidebar', () => {
test('Node preview and drag to canvas', async ({ comfyPage }) => {
const tab = comfyPage.menu.nodeLibraryTab
await expect(tab.getFolder('sampling')).toBeVisible()
await tab.getFolder('sampling').click()
// Hover over a node to display the preview
@@ -91,9 +92,13 @@ test.describe('Node library sidebar', () => {
test('Bookmark node', async ({ comfyPage }) => {
const tab = comfyPage.menu.nodeLibraryTab
await expect(tab.getFolder('sampling')).toBeVisible()
await tab.getFolder('sampling').click()
// Bookmark the node
await expect(
tab.getNode('KSampler (Advanced)').locator('.bookmark-button')
).toBeVisible()
await tab.getNode('KSampler (Advanced)').locator('.bookmark-button').click()
// Verify the bookmark is added to the bookmarks tab
@@ -124,6 +129,7 @@ test.describe('Node library sidebar', () => {
test('Can add new bookmark folder', async ({ comfyPage }) => {
const tab = comfyPage.menu.nodeLibraryTab
await expect(tab.newFolderButton).toBeVisible()
await tab.newFolderButton.click()
const textInput = comfyPage.page.locator('.editable-text input')
await textInput.waitFor({ state: 'visible' })
@@ -138,7 +144,11 @@ test.describe('Node library sidebar', () => {
const tab = comfyPage.menu.nodeLibraryTab
await expect(tab.getFolder('foo')).toBeVisible()
await expect(tab.getFolder('foo')).toBeVisible()
await tab.getFolder('foo').click({ button: 'right' })
await expect(
comfyPage.page.getByRole('menuitem', { name: 'New Folder' })
).toBeVisible()
await comfyPage.page.getByRole('menuitem', { name: 'New Folder' }).click()
const textInput = comfyPage.page.locator('.editable-text input')
await textInput.waitFor({ state: 'visible' })
@@ -154,7 +164,9 @@ test.describe('Node library sidebar', () => {
const tab = comfyPage.menu.nodeLibraryTab
await expect(tab.getFolder('foo')).toBeVisible()
await expect(tab.getFolder('foo')).toBeVisible()
await tab.getFolder('foo').click({ button: 'right' })
await expect(comfyPage.page.getByLabel('Delete')).toBeVisible()
await comfyPage.page.getByLabel('Delete').click()
await expectBookmarks(comfyPage, [])
@@ -165,6 +177,7 @@ test.describe('Node library sidebar', () => {
const tab = comfyPage.menu.nodeLibraryTab
await expect(tab.getFolder('foo')).toBeVisible()
await expect(tab.getFolder('foo')).toBeVisible()
await tab.getFolder('foo').click({ button: 'right' })
await comfyPage.page
.locator('.p-contextmenu-item-label:has-text("Rename")')
@@ -180,6 +193,7 @@ test.describe('Node library sidebar', () => {
await comfyPage.settings.setSetting(bookmarksSettingId, ['foo/'])
const tab = comfyPage.menu.nodeLibraryTab
await expect(tab.getFolder('foo')).toBeVisible()
await expect(tab.getFolder('sampling')).toBeVisible()
await tab.getFolder('sampling').click()
await comfyPage.page.dragAndDrop(
tab.nodeSelector('KSampler (Advanced)'),
@@ -192,7 +206,11 @@ test.describe('Node library sidebar', () => {
comfyPage
}) => {
const tab = comfyPage.menu.nodeLibraryTab
await expect(tab.getFolder('sampling')).toBeVisible()
await tab.getFolder('sampling').click()
await expect(
tab.getNode('KSampler (Advanced)').locator('.bookmark-button')
).toBeVisible()
await tab.getNode('KSampler (Advanced)').locator('.bookmark-button').click()
await expectBookmarks(comfyPage, ['KSamplerAdvanced'])
})
@@ -203,6 +221,9 @@ test.describe('Node library sidebar', () => {
])
const tab = comfyPage.menu.nodeLibraryTab
await expect(tab.getNode('KSampler (Advanced)')).toHaveCount(1)
await expect(
tab.getNode('KSampler (Advanced)').locator('.bookmark-button')
).toBeVisible()
await tab.getNode('KSampler (Advanced)').locator('.bookmark-button').click()
await expectBookmarks(comfyPage, [])
})
@@ -212,6 +233,7 @@ test.describe('Node library sidebar', () => {
'KSamplerAdvanced'
])
const tab = comfyPage.menu.nodeLibraryTab
await expect(tab.getFolder('sampling')).toBeVisible()
await tab.getFolder('sampling').click()
await expect(tab.getNode('KSampler (Advanced)')).toHaveCount(2)
await tab
@@ -224,20 +246,25 @@ test.describe('Node library sidebar', () => {
await comfyPage.settings.setSetting(bookmarksSettingId, ['foo/'])
const tab = comfyPage.menu.nodeLibraryTab
await expect(tab.getFolder('foo')).toBeVisible()
await expect(tab.getFolder('foo')).toBeVisible()
await tab.getFolder('foo').click({ button: 'right' })
await expect(comfyPage.page.getByLabel('Customize')).toBeVisible()
await comfyPage.page.getByLabel('Customize').click()
const dialog = comfyPage.page.getByRole('dialog', {
name: 'Customize Folder'
})
// Select Folder icon (2nd button in Icon group)
const iconGroup = dialog.getByText('Icon').locator('..').getByRole('group')
await expect(iconGroup.getByRole('button').nth(1)).toBeVisible()
await iconGroup.getByRole('button').nth(1).click()
// Select Blue color (2nd button in Color group)
const colorGroup = dialog
.getByText('Color')
.locator('..')
.getByRole('group')
await expect(colorGroup.getByRole('button').nth(1)).toBeVisible()
await colorGroup.getByRole('button').nth(1).click()
await expect(dialog.getByRole('button', { name: 'Confirm' })).toBeVisible()
await dialog.getByRole('button', { name: 'Confirm' }).click()
await comfyPage.nextFrame()
await expectBookmarkCustomization(comfyPage, {
@@ -252,14 +279,18 @@ test.describe('Node library sidebar', () => {
await comfyPage.settings.setSetting(bookmarksSettingId, ['foo/'])
const tab = comfyPage.menu.nodeLibraryTab
await expect(tab.getFolder('foo')).toBeVisible()
await expect(tab.getFolder('foo')).toBeVisible()
await tab.getFolder('foo').click({ button: 'right' })
await expect(comfyPage.page.getByLabel('Customize')).toBeVisible()
await comfyPage.page.getByLabel('Customize').click()
const dialog = comfyPage.page.getByRole('dialog', {
name: 'Customize Folder'
})
// Select Folder icon (2nd button in Icon group)
const iconGroup = dialog.getByText('Icon').locator('..').getByRole('group')
await expect(iconGroup.getByRole('button').nth(1)).toBeVisible()
await iconGroup.getByRole('button').nth(1).click()
await expect(dialog.getByRole('button', { name: 'Confirm' })).toBeVisible()
await dialog.getByRole('button', { name: 'Confirm' }).click()
await comfyPage.nextFrame()
await expectBookmarkCustomization(comfyPage, {
@@ -276,13 +307,16 @@ test.describe('Node library sidebar', () => {
await comfyPage.settings.setSetting(bookmarksSettingId, ['foo/'])
const tab = comfyPage.menu.nodeLibraryTab
await expect(tab.getFolder('foo')).toBeVisible()
await expect(tab.getFolder('foo')).toBeVisible()
await tab.getFolder('foo').click({ button: 'right' })
await expect(comfyPage.page.getByLabel('Customize')).toBeVisible()
await comfyPage.page.getByLabel('Customize').click()
// Click a color option multiple times
const customColorOption = comfyPage.page.locator(
'.p-togglebutton-content > .pi-palette'
)
await expect(customColorOption).toBeVisible()
await customColorOption.click()
await customColorOption.click()
@@ -291,6 +325,9 @@ test.describe('Node library sidebar', () => {
.getByLabel('Customize Folder')
.getByRole('textbox')
.click()
await expect(
comfyPage.page.locator('.p-colorpicker-color-background')
).toBeVisible()
await comfyPage.page.locator('.p-colorpicker-color-background').click()
// Finalize the customization
@@ -299,7 +336,9 @@ test.describe('Node library sidebar', () => {
})
// Select Folder icon (2nd button in Icon group)
const iconGroup = dialog.getByText('Icon').locator('..').getByRole('group')
await expect(iconGroup.getByRole('button').nth(1)).toBeVisible()
await iconGroup.getByRole('button').nth(1).click()
await expect(dialog.getByRole('button', { name: 'Confirm' })).toBeVisible()
await dialog.getByRole('button', { name: 'Confirm' }).click()
await comfyPage.nextFrame()
@@ -327,6 +366,7 @@ test.describe('Node library sidebar', () => {
})
const tab = comfyPage.menu.nodeLibraryTab
await expect(tab.getFolder('foo')).toBeVisible()
await expect(tab.getFolder('foo')).toBeVisible()
await tab.getFolder('foo').click({ button: 'right' })
await comfyPage.page
.locator('.p-contextmenu-item-label:has-text("Rename")')
@@ -365,7 +405,9 @@ test.describe('Node library sidebar', () => {
})
const tab = comfyPage.menu.nodeLibraryTab
await expect(tab.getFolder('foo')).toBeVisible()
await expect(tab.getFolder('foo')).toBeVisible()
await tab.getFolder('foo').click({ button: 'right' })
await expect(comfyPage.page.getByLabel('Delete')).toBeVisible()
await comfyPage.page.getByLabel('Delete').click()
await comfyPage.nextFrame()
await expectBookmarks(comfyPage, [])

View File

@@ -16,10 +16,12 @@ test.describe('Node library sidebar V2', () => {
await expect(tab.allTab).toHaveAttribute('aria-selected', 'true')
await expect(tab.blueprintsTab).toBeVisible()
await tab.blueprintsTab.click()
await expect(tab.blueprintsTab).toHaveAttribute('aria-selected', 'true')
await expect(tab.allTab).toHaveAttribute('aria-selected', 'false')
await expect(tab.allTab).toBeVisible()
await tab.allTab.click()
await expect(tab.allTab).toHaveAttribute('aria-selected', 'true')
await expect(tab.blueprintsTab).toHaveAttribute('aria-selected', 'false')
@@ -114,6 +116,7 @@ test.describe('Node library sidebar V2', () => {
test('Sort dropdown shows sorting options', async ({ comfyPage }) => {
const tab = comfyPage.menu.nodeLibraryTabV2
await expect(tab.sortButton).toBeVisible()
await tab.sortButton.click()
// Reka UI DropdownMenuRadioItem renders with role="menuitemradio"

View File

@@ -89,6 +89,7 @@ test.describe('Workflows sidebar', () => {
.poll(() => comfyPage.nodeOps.getNodeCount())
.toEqual(originalNodeCount + 1)
await expect(tab.getPersistedItem('workflow1')).toBeVisible()
await tab.getPersistedItem('workflow1').click()
await expect.poll(() => comfyPage.nodeOps.getNodeCount()).toEqual(1)
})
@@ -105,8 +106,10 @@ test.describe('Workflows sidebar', () => {
const tab = comfyPage.menu.workflowsTab
await tab.open()
// Switch to the parent folder
await expect(tab.getPersistedItem('foo')).toBeVisible()
await tab.getPersistedItem('foo').click()
// Switch to the nested workflow
await expect(tab.getPersistedItem('bar')).toBeVisible()
await tab.getPersistedItem('bar').click()
const openedWorkflow = tab.getOpenedItem('foo/bar')
@@ -247,6 +250,9 @@ test.describe('Workflows sidebar', () => {
await expect(errorOverlay).toBeVisible()
// Dismiss the error overlay
await expect(
errorOverlay.getByTestId(TestIds.dialogs.errorOverlayDismiss)
).toBeVisible()
await errorOverlay.getByTestId(TestIds.dialogs.errorOverlayDismiss).click()
await expect(errorOverlay).not.toBeVisible()
@@ -270,6 +276,7 @@ test.describe('Workflows sidebar', () => {
const closeButton = comfyPage.page.locator(
'.comfyui-workflows-open .close-workflow-button'
)
await expect(closeButton).toBeVisible()
await closeButton.click()
await expect
.poll(() => comfyPage.menu.workflowsTab.getOpenedWorkflowNames(), {
@@ -294,6 +301,7 @@ test.describe('Workflows sidebar', () => {
await topbar.saveWorkflow(filename)
expect(await workflowsTab.getOpenedWorkflowNames()).toEqual([filename])
await expect(workflowsTab.getOpenedItem(filename)).toBeVisible()
await workflowsTab.getOpenedItem(filename).click({ button: 'right' })
await comfyPage.nextFrame()
await comfyPage.contextMenu.clickMenuItem('Delete')
@@ -312,6 +320,7 @@ test.describe('Workflows sidebar', () => {
await topbar.saveWorkflow(filename)
expect(await workflowsTab.getOpenedWorkflowNames()).toEqual([filename])
await expect(workflowsTab.getOpenedItem(filename)).toBeVisible()
await workflowsTab.getOpenedItem(filename).click({ button: 'right' })
await comfyPage.contextMenu.clickMenuItem('Delete')
await comfyPage.nextFrame()
@@ -332,6 +341,7 @@ test.describe('Workflows sidebar', () => {
const { workflowsTab } = comfyPage.menu
await workflowsTab.open()
await expect(workflowsTab.getPersistedItem('workflow1')).toBeVisible()
await workflowsTab.getPersistedItem('workflow1').click({ button: 'right' })
await comfyPage.contextMenu.clickMenuItem('Duplicate')
await expect

View File

@@ -9,6 +9,7 @@ import { TestIds } from '@e2e/fixtures/selectors'
async function ensurePropertiesPanel(comfyPage: ComfyPage) {
const panel = comfyPage.menu.propertiesPanel.root
if (!(await panel.isVisible())) {
await expect(comfyPage.actionbar.propertiesButton).toBeVisible()
await comfyPage.actionbar.propertiesButton.click()
}
await expect(panel).toBeVisible()
@@ -135,6 +136,7 @@ test.describe(
TestIds.subgraphEditor.widgetActionsMenuButton
)
await expect(moreButtons.first()).toBeVisible()
await expect(moreButtons.first()).toBeVisible()
await moreButtons.first().click()
const menu = comfyPage.page.getByTestId(TestIds.menu.moreMenuContent)

View File

@@ -45,6 +45,7 @@ test.describe('Toast Notifications', { tag: '@ui' }, () => {
await expect(comfyPage.toast.visibleToasts.first()).toBeVisible()
const closeButton = comfyPage.page.locator('.p-toast-close-button').first()
await expect(closeButton).toBeVisible()
await closeButton.click()
await expect(comfyPage.toast.visibleToasts).toHaveCount(0)

View File

@@ -23,6 +23,7 @@ test.describe('Workflow tabs', () => {
expect(await topbar.getTabNames()).toHaveLength(1)
await expect(topbar.newWorkflowButton).toBeVisible()
await topbar.newWorkflowButton.click()
await expect.poll(() => topbar.getTabNames()).toHaveLength(2)
@@ -33,12 +34,14 @@ test.describe('Workflow tabs', () => {
test('Switching tabs changes active workflow', async ({ comfyPage }) => {
const topbar = comfyPage.menu.topbar
await expect(topbar.newWorkflowButton).toBeVisible()
await topbar.newWorkflowButton.click()
await expect.poll(() => topbar.getTabNames()).toHaveLength(2)
const activeNameBefore = await topbar.getActiveTabName()
expect(activeNameBefore).toContain('Unsaved Workflow (2)')
await expect(topbar.getTab(0)).toBeVisible()
await topbar.getTab(0).click()
await expect
.poll(() => topbar.getActiveTabName())
@@ -51,6 +54,7 @@ test.describe('Workflow tabs', () => {
test('Closing a tab removes it', async ({ comfyPage }) => {
const topbar = comfyPage.menu.topbar
await expect(topbar.newWorkflowButton).toBeVisible()
await topbar.newWorkflowButton.click()
await expect.poll(() => topbar.getTabNames()).toHaveLength(2)
@@ -64,6 +68,7 @@ test.describe('Workflow tabs', () => {
test('Right-clicking a tab shows context menu', async ({ comfyPage }) => {
const topbar = comfyPage.menu.topbar
await expect(topbar.getTab(0)).toBeVisible()
await topbar.getTab(0).click({ button: 'right' })
// Reka UI ContextMenuContent gets data-state="open" when active
@@ -85,9 +90,11 @@ test.describe('Workflow tabs', () => {
}) => {
const topbar = comfyPage.menu.topbar
await expect(topbar.newWorkflowButton).toBeVisible()
await topbar.newWorkflowButton.click()
await expect.poll(() => topbar.getTabNames()).toHaveLength(2)
await expect(topbar.getTab(1)).toBeVisible()
await topbar.getTab(1).click({ button: 'right' })
const contextMenu = comfyPage.page.locator(
'[role="menu"][data-state="open"]'
@@ -136,12 +143,14 @@ test.describe('Workflow tabs', () => {
const topbar = comfyPage.menu.topbar
// Create 2 additional tabs (3 total)
await expect(topbar.newWorkflowButton).toBeVisible()
await topbar.newWorkflowButton.click()
await expect.poll(() => topbar.getTabNames()).toHaveLength(2)
await topbar.newWorkflowButton.click()
await expect.poll(() => topbar.getTabNames()).toHaveLength(3)
// Switch to first tab
await expect(topbar.getTab(0)).toBeVisible()
await topbar.getTab(0).click()
await expect
.poll(() => topbar.getActiveTabName())

View File

@@ -91,6 +91,7 @@ test.describe('Settings Search functionality', { tag: '@settings' }, () => {
const categoryCount = await dialog.categories.count()
if (categoryCount > 1) {
await expect(dialog.categories.nth(1)).toBeVisible()
await dialog.categories.nth(1).click()
await expect(dialog.categories.nth(1)).toHaveClass(

View File

@@ -28,6 +28,7 @@ test.describe('User Select View', { tag: '@settings' }, () => {
await page.goto(userSelectPage.url)
await expect(page).toHaveURL(userSelectPage.selectionUrl)
await userSelectPage.newUserInput.fill(randomUser)
await expect(userSelectPage.nextButton).toBeVisible()
await userSelectPage.nextButton.click()
await expect(page).toHaveURL(userSelectPage.url)
})
@@ -35,8 +36,13 @@ test.describe('User Select View', { tag: '@settings' }, () => {
test('Can choose existing user', async ({ userSelectPage, page }) => {
await page.goto(userSelectPage.url)
await expect(page).toHaveURL(userSelectPage.selectionUrl)
await expect(userSelectPage.existingUserSelect).toBeVisible()
await userSelectPage.existingUserSelect.click()
await expect(
page.locator('.p-select-list .p-select-option').first()
).toBeVisible()
await page.locator('.p-select-list .p-select-option').first().click()
await expect(userSelectPage.nextButton).toBeVisible()
await userSelectPage.nextButton.click()
await expect(page).toHaveURL(userSelectPage.url)
})

View File

@@ -108,6 +108,7 @@ test.describe('Version Mismatch Warnings', { tag: '@slow' }, () => {
})
await warningToast.waitFor({ state: 'visible' })
const dismissButton = warningToast.getByRole('button', { name: 'Close' })
await expect(dismissButton).toBeVisible()
await dismissButton.click()
// Wait for the dismissed state to be persisted

View File

@@ -120,7 +120,9 @@ test.describe('Vue Node Groups', { tag: '@screenshot' }, () => {
})
test('should allow creating groups with hotkey', async ({ comfyPage }) => {
await expect(comfyPage.page.getByText('Load Checkpoint')).toBeVisible()
await comfyPage.page.getByText('Load Checkpoint').click()
await expect(comfyPage.page.getByText('KSampler')).toBeVisible()
await comfyPage.page.getByText('KSampler').click({ modifiers: ['Control'] })
await comfyPage.page.keyboard.press(CREATE_GROUP_HOTKEY)
await comfyPage.nextFrame()

View File

@@ -31,6 +31,7 @@ async function openMultiNodeContextMenu(
for (const title of titles) {
const fixture = await comfyPage.vueNodes.getFixtureByTitle(title)
await expect(fixture.header).toBeVisible()
await fixture.header.click({ modifiers: ['ControlOrMeta'] })
}
await comfyPage.nextFrame()
@@ -356,6 +357,7 @@ test.describe('Vue Node Context Menu', () => {
await comfyPage.nodeOps.fillPromptDialog('TestBlueprint')
// Open node library sidebar and search for the blueprint
await expect(comfyPage.menu.nodeLibraryTab.tabButton).toBeVisible()
await comfyPage.menu.nodeLibraryTab.tabButton.click()
const searchBox = comfyPage.page.getByRole('combobox', {
name: 'Search'

View File

@@ -44,6 +44,7 @@ test.describe('Vue Nodes Image Preview', () => {
const { imagePreview } = await loadImageOnNode(comfyPage)
await imagePreview.getByRole('region').hover()
await expect(comfyPage.page.getByLabel('Edit or mask image')).toBeVisible()
await comfyPage.page.getByLabel('Edit or mask image').click()
await expect(comfyPage.page.locator('.mask-editor-dialog')).toBeVisible()
@@ -56,6 +57,7 @@ test.describe('Vue Nodes Image Preview', () => {
const nodeHeader = comfyPage.vueNodes
.getNodeLocator(nodeId)
.locator('.lg-node-header')
await expect(nodeHeader).toBeVisible()
await nodeHeader.click({ button: 'right' })
const contextMenu = comfyPage.page.locator('.p-contextmenu')

View File

@@ -91,6 +91,7 @@ test.describe('Vue Nodes - Delete Key Interaction', () => {
.first()
// Click on text widget to focus it
await expect(textWidget).toBeVisible()
await textWidget.click()
await textWidget.fill('test text')

View File

@@ -18,6 +18,7 @@ test.describe('Vue Node Resizing', () => {
if (!initialBox) throw new Error('Node bounding box not found')
// Select the node first (this was causing the bug)
await expect(node.header).toBeVisible()
await node.header.click()
// Get position after selection

View File

@@ -23,14 +23,17 @@ test.describe('Vue Node Selection', () => {
test(`should allow selecting multiple nodes with ${name}+click`, async ({
comfyPage
}) => {
await expect(comfyPage.page.getByText('Load Checkpoint')).toBeVisible()
await comfyPage.page.getByText('Load Checkpoint').click()
await expect.poll(() => comfyPage.vueNodes.getSelectedNodeCount()).toBe(1)
await expect(comfyPage.page.getByText('Empty Latent Image')).toBeVisible()
await comfyPage.page.getByText('Empty Latent Image').click({
modifiers: [modifier]
})
await expect.poll(() => comfyPage.vueNodes.getSelectedNodeCount()).toBe(2)
await expect(comfyPage.page.getByText('KSampler')).toBeVisible()
await comfyPage.page.getByText('KSampler').click({
modifiers: [modifier]
})
@@ -40,9 +43,11 @@ test.describe('Vue Node Selection', () => {
test(`should allow de-selecting nodes with ${name}+click`, async ({
comfyPage
}) => {
await expect(comfyPage.page.getByText('Load Checkpoint')).toBeVisible()
await comfyPage.page.getByText('Load Checkpoint').click()
await expect.poll(() => comfyPage.vueNodes.getSelectedNodeCount()).toBe(1)
await expect(comfyPage.page.getByText('Load Checkpoint')).toBeVisible()
await comfyPage.page.getByText('Load Checkpoint').click({
modifiers: [modifier]
})
@@ -68,6 +73,7 @@ test.describe('Vue Node Selection', () => {
const PIN_INDICATOR = '[data-testid="node-pin-indicator"]'
const checkpointNodeHeader = comfyPage.page.getByText('Load Checkpoint')
await expect(checkpointNodeHeader).toBeVisible()
await checkpointNodeHeader.click()
await comfyPage.page.keyboard.press(PIN_HOTKEY)

View File

@@ -19,6 +19,7 @@ test.describe('Vue Node Bypass', () => {
'should allow toggling bypass on a selected node with hotkey',
{ tag: '@screenshot' },
async ({ comfyPage }) => {
await expect(comfyPage.page.getByText('Load Checkpoint')).toBeVisible()
await comfyPage.page.getByText('Load Checkpoint').click()
await comfyPage.page.keyboard.press(BYPASS_HOTKEY)
@@ -40,7 +41,9 @@ test.describe('Vue Node Bypass', () => {
test('should allow toggling bypass on multiple selected nodes with hotkey', async ({
comfyPage
}) => {
await expect(comfyPage.page.getByText('Load Checkpoint')).toBeVisible()
await comfyPage.page.getByText('Load Checkpoint').click()
await expect(comfyPage.page.getByText('KSampler')).toBeVisible()
await comfyPage.page.getByText('KSampler').click({ modifiers: ['Control'] })
const checkpointNode = comfyPage.page

View File

@@ -18,11 +18,13 @@ test.describe('Vue Node Custom Colors', { tag: '@screenshot' }, () => {
const loadCheckpointNode = comfyPage.page.locator('[data-node-id]').filter({
hasText: 'Load Checkpoint'
})
await expect(loadCheckpointNode.getByText('Load Checkpoint')).toBeVisible()
await loadCheckpointNode.getByText('Load Checkpoint').click()
const colorPickerButton = comfyPage.page.getByTestId(
TestIds.selectionToolbox.colorPickerButton
)
await expect(colorPickerButton).toBeVisible()
await colorPickerButton.click()
const colorPickerGroup = comfyPage.page.getByRole('group').filter({

View File

@@ -30,6 +30,7 @@ test.describe('Vue Node Error', () => {
}) => {
await comfyPage.setup()
await comfyPage.workflow.loadWorkflow('nodes/execution_error')
await expect(comfyPage.runButton).toBeVisible()
await comfyPage.runButton.click()
const raiseErrorNode = comfyPage.page

View File

@@ -16,6 +16,7 @@ test.describe('Vue Node Mute', () => {
'should allow toggling mute on a selected node with hotkey',
{ tag: '@screenshot' },
async ({ comfyPage }) => {
await expect(comfyPage.page.getByText('Load Checkpoint')).toBeVisible()
await comfyPage.page.getByText('Load Checkpoint').click()
await comfyPage.page.keyboard.press(MUTE_HOTKEY)
@@ -34,7 +35,9 @@ test.describe('Vue Node Mute', () => {
test('should allow toggling mute on multiple selected nodes with hotkey', async ({
comfyPage
}) => {
await expect(comfyPage.page.getByText('Load Checkpoint')).toBeVisible()
await comfyPage.page.getByText('Load Checkpoint').click()
await expect(comfyPage.page.getByText('KSampler')).toBeVisible()
await comfyPage.page.getByText('KSampler').click({ modifiers: ['Control'] })
const checkpointNode = comfyPage.vueNodes.getNodeByTitle('Load Checkpoint')

View File

@@ -15,6 +15,7 @@ test.describe('Vue Node Pin', () => {
test('should allow toggling pin on a selected node with hotkey', async ({
comfyPage
}) => {
await expect(comfyPage.page.getByText('Load Checkpoint')).toBeVisible()
await comfyPage.page.getByText('Load Checkpoint').click()
await comfyPage.page.keyboard.press(PIN_HOTKEY)
@@ -30,7 +31,9 @@ test.describe('Vue Node Pin', () => {
test('should allow toggling pin on multiple selected nodes with hotkey', async ({
comfyPage
}) => {
await expect(comfyPage.page.getByText('Load Checkpoint')).toBeVisible()
await comfyPage.page.getByText('Load Checkpoint').click()
await expect(comfyPage.page.getByText('KSampler')).toBeVisible()
await comfyPage.page.getByText('KSampler').click({ modifiers: ['Control'] })
const checkpointNode = comfyPage.vueNodes.getNodeByTitle('Load Checkpoint')
@@ -49,6 +52,7 @@ test.describe('Vue Node Pin', () => {
test('should not allow dragging pinned nodes', async ({ comfyPage }) => {
const checkpointNodeHeader = comfyPage.page.getByText('Load Checkpoint')
await expect(checkpointNodeHeader).toBeVisible()
await checkpointNodeHeader.click()
await comfyPage.page.keyboard.press(PIN_HOTKEY)
@@ -67,6 +71,7 @@ test.describe('Vue Node Pin', () => {
expect(headerPosAfterDrag).toEqual(headerPos)
// Unpin the node with the hotkey
await expect(checkpointNodeHeader).toBeVisible()
await checkpointNodeHeader.click()
await comfyPage.page.keyboard.press(PIN_HOTKEY)

View File

@@ -63,6 +63,7 @@ test.describe('Advanced Widget Visibility', () => {
await expect(widgets).toHaveCount(2)
// Click the toggle button to show advanced widgets
await expect(node.getByText('Show advanced inputs')).toBeVisible()
await node.getByText('Show advanced inputs').click()
await expect(widgets).toHaveCount(4)
@@ -73,6 +74,7 @@ test.describe('Advanced Widget Visibility', () => {
await expect(node.getByText('Hide advanced inputs')).toBeVisible()
// Click again to hide
await expect(node.getByText('Hide advanced inputs')).toBeVisible()
await node.getByText('Hide advanced inputs').click()
await expect(widgets).toHaveCount(2)
})

View File

@@ -40,9 +40,11 @@ test.describe('Vue Integer Widget', () => {
await comfyPage.vueNodes.deleteSelected()
// Test widget works when unlinked
await expect(controls.incrementButton).toBeVisible()
await controls.incrementButton.click()
await expect(controls.input).toHaveValue((initialValue + 1).toString())
await expect(controls.decrementButton).toBeVisible()
await controls.decrementButton.click()
await expect(controls.input).toHaveValue(initialValue.toString())
})

Some files were not shown because too many files have changed in this diff Show More