Split ComfyPage fixture (#1305)

* Split down page components

* Move litegraph utils

* nit
This commit is contained in:
Chenlei Hu
2024-10-25 08:29:02 -04:00
committed by GitHub
parent 624bcc75ab
commit 59c999324e
8 changed files with 543 additions and 534 deletions

View File

@@ -0,0 +1,79 @@
import { Locator, Page } from '@playwright/test'
export class ComfyNodeSearchFilterSelectionPanel {
constructor(public readonly page: Page) {}
async selectFilterType(filterType: string) {
await this.page
.locator(
`.filter-type-select .p-togglebutton-label:has-text("${filterType}")`
)
.click()
}
async selectFilterValue(filterValue: string) {
await this.page.locator('.filter-value-select .p-select-dropdown').click()
await this.page
.locator(
`.p-select-overlay .p-select-list .p-select-option-label:text-is("${filterValue}")`
)
.click()
}
async addFilter(filterValue: string, filterType: string) {
await this.selectFilterType(filterType)
await this.selectFilterValue(filterValue)
await this.page.locator('.p-button-label:has-text("Add")').click()
}
}
export class ComfyNodeSearchBox {
public readonly input: Locator
public readonly dropdown: Locator
public readonly filterSelectionPanel: ComfyNodeSearchFilterSelectionPanel
constructor(public readonly page: Page) {
this.input = page.locator(
'.comfy-vue-node-search-container input[type="text"]'
)
this.dropdown = page.locator(
'.comfy-vue-node-search-container .p-autocomplete-list'
)
this.filterSelectionPanel = new ComfyNodeSearchFilterSelectionPanel(page)
}
get filterButton() {
return this.page.locator('.comfy-vue-node-search-container ._filter-button')
}
async fillAndSelectFirstNode(
nodeName: string,
options?: { suggestionIndex: number }
) {
await this.input.waitFor({ state: 'visible' })
await this.input.fill(nodeName)
await this.dropdown.waitFor({ state: 'visible' })
// Wait for some time for the auto complete list to update.
// The auto complete list is debounced and may take some time to update.
await this.page.waitForTimeout(500)
await this.dropdown
.locator('li')
.nth(options?.suggestionIndex || 0)
.click()
}
async addFilter(filterValue: string, filterType: string) {
await this.filterButton.click()
await this.filterSelectionPanel.addFilter(filterValue, filterType)
}
get filterChips() {
return this.page.locator(
'.comfy-vue-node-search-container .p-autocomplete-chip-item'
)
}
async removeFilter(index: number) {
await this.filterChips.nth(index).locator('.p-chip-remove-icon').click()
}
}

View File

@@ -0,0 +1,120 @@
import { Page } from '@playwright/test'
class SidebarTab {
constructor(
public readonly page: Page,
public readonly tabId: string
) {}
get tabButton() {
return this.page.locator(`.${this.tabId}-tab-button`)
}
get selectedTabButton() {
return this.page.locator(
`.${this.tabId}-tab-button.side-bar-button-selected`
)
}
async open() {
if (await this.selectedTabButton.isVisible()) {
return
}
await this.tabButton.click()
}
}
export class NodeLibrarySidebarTab extends SidebarTab {
constructor(public readonly page: Page) {
super(page, 'node-library')
}
get nodeLibrarySearchBoxInput() {
return this.page.locator('.node-lib-search-box input[type="text"]')
}
get nodeLibraryTree() {
return this.page.locator('.node-lib-tree-explorer')
}
get nodePreview() {
return this.page.locator('.node-lib-node-preview')
}
get tabContainer() {
return this.page.locator('.sidebar-content-container')
}
get newFolderButton() {
return this.tabContainer.locator('.new-folder-button')
}
async open() {
await super.open()
await this.nodeLibraryTree.waitFor({ state: 'visible' })
}
async close() {
if (!this.tabButton.isVisible()) {
return
}
await this.tabButton.click()
await this.nodeLibraryTree.waitFor({ state: 'hidden' })
}
folderSelector(folderName: string) {
return `.p-tree-node-content:has(> .tree-explorer-node-label:has(.tree-folder .node-label:has-text("${folderName}")))`
}
getFolder(folderName: string) {
return this.page.locator(this.folderSelector(folderName))
}
nodeSelector(nodeName: string) {
return `.p-tree-node-content:has(> .tree-explorer-node-label:has(.tree-leaf .node-label:has-text("${nodeName}")))`
}
getNode(nodeName: string) {
return this.page.locator(this.nodeSelector(nodeName))
}
}
export class WorkflowsSidebarTab extends SidebarTab {
constructor(public readonly page: Page) {
super(page, 'workflows')
}
get browseGalleryButton() {
return this.page.locator('.browse-templates-button')
}
get newBlankWorkflowButton() {
return this.page.locator('.new-blank-workflow-button')
}
get openWorkflowButton() {
return this.page.locator('.open-workflow-button')
}
async getOpenedWorkflowNames() {
return await this.page
.locator('.comfyui-workflows-open .node-label')
.allInnerTexts()
}
async getTopLevelSavedWorkflowNames() {
return await this.page
.locator('.comfyui-workflows-browse .node-label')
.allInnerTexts()
}
async switchToWorkflow(workflowName: string) {
const workflowLocator = this.page.locator(
'.comfyui-workflows-open .node-label',
{ hasText: workflowName }
)
await workflowLocator.click()
await this.page.waitForTimeout(300)
}
}

View File

@@ -0,0 +1,66 @@
import { Locator, Page } from '@playwright/test'
export class Topbar {
constructor(public readonly page: Page) {}
async getTabNames(): Promise<string[]> {
return await this.page
.locator('.workflow-tabs .workflow-label')
.allInnerTexts()
}
async openSubmenuMobile() {
await this.page.locator('.p-menubar-mobile .p-menubar-button').click()
}
async getMenuItem(itemLabel: string): Promise<Locator> {
return this.page.locator(`.p-menubar-item-label:text-is("${itemLabel}")`)
}
async getWorkflowTab(tabName: string): Promise<Locator> {
return this.page
.locator(`.workflow-tabs .workflow-label:has-text("${tabName}")`)
.locator('..')
}
async closeWorkflowTab(tabName: string) {
const tab = await this.getWorkflowTab(tabName)
await tab.locator('.close-button').click({ force: true })
}
async saveWorkflow(workflowName: string) {
await this.triggerTopbarCommand(['Workflow', 'Save'])
await this.page.locator('.p-dialog-content input').fill(workflowName)
await this.page.keyboard.press('Enter')
// Wait for the dialog to close.
await this.page.waitForTimeout(300)
}
async triggerTopbarCommand(path: string[]) {
if (path.length < 2) {
throw new Error('Path is too short')
}
const tabName = path[0]
const topLevelMenu = this.page.locator(
`.top-menubar .p-menubar-item-label:text-is("${tabName}")`
)
await topLevelMenu.waitFor({ state: 'visible' })
await topLevelMenu.click()
for (let i = 1; i < path.length; i++) {
const commandName = path[i]
const menuItem = this.page
.locator(
`.top-menubar .p-menubar-submenu .p-menubar-item:has-text("${commandName}")`
)
.first()
await menuItem.waitFor({ state: 'visible' })
await menuItem.hover()
if (i === path.length - 1) {
await menuItem.click()
}
}
}
}