mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-06-08 15:29:52 +00:00
## Summary Narrow Knip's Playwright `entry` to actual spec files so dead exports in browser-test fixtures are reported instead of being hidden by treating every helper as an entrypoint. ## Changes - **What**: - `knip.config.ts`: Playwright `entry` changed from the broad `['**/*.@(spec|test)…', 'browser_tests/**/*.ts']` to `['browser_tests/**/*.@(spec|test).?(c|m)[jt]s?(x)']`. `globalSetup`/`globalTeardown` stay covered via Knip's playwright config resolution; fixtures remain in the project graph so their unused exports surface. - Resolved the 54 dead findings this exposed: over-exported symbols used only within their own module are now module-private (dropped `export`, no behavioral change); genuinely unreferenced fixtures were deleted (asset/template `ALL_*` aggregators + orphaned `STABLE_*` data, `TemplateHelper` distribution helpers + `generateTemplates`, dead types/utils, and the unused `nodeDefinitions.ts` module). - **Breaking**: none — test-only changes. ## Review Focus - Deletions are limited to fixtures with zero importers on `main` (verified via `pnpm knip`); the bulk of the diff is `export`-keyword removal. - Verified: `pnpm knip` (browser_tests clean), `pnpm typecheck`, `pnpm typecheck:browser`, oxfmt/oxlint/eslint all pass. Linear: FE-717
124 lines
4.3 KiB
TypeScript
124 lines
4.3 KiB
TypeScript
import { expect } from '@playwright/test'
|
|
import type { Locator } from '@playwright/test'
|
|
|
|
import type { RootCategoryId } from '@/components/searchbox/v2/rootCategories'
|
|
import type { ComfyPage } from '@e2e/fixtures/ComfyPage'
|
|
import { TestIds } from '@e2e/fixtures/selectors'
|
|
import type { Position } from '@e2e/fixtures/types'
|
|
|
|
const { searchBoxV2 } = TestIds
|
|
|
|
export class ComfyNodeSearchBoxV2 {
|
|
readonly dialog: Locator
|
|
readonly input: Locator
|
|
readonly filterSearch: Locator
|
|
readonly results: Locator
|
|
readonly filterOptions: Locator
|
|
readonly filterChips: Locator
|
|
readonly noResults: Locator
|
|
readonly nodeIdBadge: Locator
|
|
readonly sidebarToggle: Locator
|
|
readonly sidebarBackdrop: Locator
|
|
readonly filterChipsScroll: Locator
|
|
|
|
constructor(private comfyPage: ComfyPage) {
|
|
const page = comfyPage.page
|
|
this.dialog = page.getByRole('search')
|
|
this.input = this.dialog.getByRole('combobox')
|
|
this.filterSearch = this.dialog.getByRole('textbox', { name: 'Search' })
|
|
this.results = this.dialog.getByTestId(searchBoxV2.resultItem)
|
|
this.filterOptions = this.dialog.getByTestId(searchBoxV2.filterOption)
|
|
this.filterChips = this.dialog.getByTestId(searchBoxV2.filterChip)
|
|
this.noResults = this.dialog.getByTestId(searchBoxV2.noResults)
|
|
this.nodeIdBadge = this.dialog.getByTestId(searchBoxV2.nodeIdBadge)
|
|
this.sidebarToggle = this.dialog.getByTestId(searchBoxV2.sidebarToggle)
|
|
this.sidebarBackdrop = this.dialog.getByTestId(searchBoxV2.sidebarBackdrop)
|
|
this.filterChipsScroll = this.dialog.getByTestId(
|
|
searchBoxV2.filterChipsScroll
|
|
)
|
|
}
|
|
|
|
/** Sidebar category tree button (e.g. `sampling`, `sampling/custom_sampling`). */
|
|
categoryButton(categoryId: string): Locator {
|
|
return this.dialog.getByTestId(searchBoxV2.category(categoryId))
|
|
}
|
|
|
|
/** Top filter-bar root category chip (e.g. `comfy`, `essentials`). */
|
|
rootCategoryButton(id: RootCategoryId): Locator {
|
|
return this.dialog.getByTestId(searchBoxV2.rootCategory(id))
|
|
}
|
|
|
|
/** Top filter-bar input/output type popover trigger. */
|
|
typeFilterButton(key: 'input' | 'output'): Locator {
|
|
return this.dialog.getByTestId(searchBoxV2.typeFilter(key))
|
|
}
|
|
|
|
async applyTypeFilter(
|
|
key: 'input' | 'output',
|
|
typeName: string
|
|
): Promise<void> {
|
|
const trigger = this.typeFilterButton(key)
|
|
await trigger.click()
|
|
await this.filterOptions.first().waitFor({ state: 'visible' })
|
|
await this.filterSearch.fill(typeName)
|
|
await this.filterOptions.filter({ hasText: typeName }).first().click()
|
|
// The popover does not auto-close on selection — toggle the trigger.
|
|
await trigger.click()
|
|
await this.filterOptions.first().waitFor({ state: 'hidden' })
|
|
}
|
|
|
|
async removeFilterChip(index = 0): Promise<void> {
|
|
await this.filterChips
|
|
.nth(index)
|
|
.getByTestId(searchBoxV2.chipDelete)
|
|
.click()
|
|
}
|
|
|
|
async toggle(): Promise<void> {
|
|
await this.comfyPage.command.executeCommand('Workspace.SearchBox.Toggle')
|
|
}
|
|
|
|
async open(): Promise<void> {
|
|
if (await this.input.isVisible()) return
|
|
await this.toggle()
|
|
await this.input.waitFor({ state: 'visible' })
|
|
}
|
|
|
|
async openByDoubleClickCanvas(position?: Position) {
|
|
const { x, y } = position ?? { x: 200, y: 200 }
|
|
// Use page.mouse.dblclick (not canvas.dblclick) so the z-999 Vue overlay
|
|
// does not intercept; coords target a viewport spot that is on the canvas
|
|
// and clear of both the side toolbar and any default-graph nodes.
|
|
await this.comfyPage.page.mouse.dblclick(x, y, { delay: 5 })
|
|
}
|
|
|
|
async ensureV2Search(): Promise<void> {
|
|
await this.comfyPage.settings.setSetting(
|
|
'Comfy.NodeSearchBoxImpl',
|
|
'default'
|
|
)
|
|
}
|
|
|
|
async setup(): Promise<void> {
|
|
await this.ensureV2Search()
|
|
await this.comfyPage.settings.setSetting(
|
|
'Comfy.LinkRelease.Action',
|
|
'search box'
|
|
)
|
|
await this.comfyPage.settings.setSetting(
|
|
'Comfy.LinkRelease.ActionShift',
|
|
'search box'
|
|
)
|
|
}
|
|
|
|
async addNode(query: string, options: { position?: Position } = {}) {
|
|
const position = options.position ?? { x: 200, y: 200 }
|
|
await this.openByDoubleClickCanvas(position)
|
|
await this.input.fill(query)
|
|
await expect(this.results.first()).toContainText(query)
|
|
await this.comfyPage.page.keyboard.press('Enter')
|
|
await expect(this.dialog).toBeHidden()
|
|
await this.comfyPage.page.mouse.click(position.x, position.y)
|
|
}
|
|
}
|