Compare commits

...

1 Commits

Author SHA1 Message Date
Johnpaul
08e6852741 test: replace raw CSS selectors with TestIds in context menu spec
- Add pinIndicator, innerWrapper, titleEditorInput, mainImage to TestIds.node
- Add pinIndicator getter to VueNodeFixture
- Add data-testid to TitleEditor overlay via input-attrs
- Replace .lg-node-header with [data-testid^="node-header-"]
- Replace .p-contextmenu with comfyPage.contextMenu.primeVueMenu
- Replace .node-title-editor input with getByTestId(TestIds.node.titleEditorInput)
- Replace hardcoded PIN_INDICATOR with TestIds.node.pinIndicator
- Replace .image-preview img with TestIds.node.mainImage
- Use NodeLibrarySidebarTab fixture for node library interaction

Fixes #10750
Fixes #10749
2026-03-30 21:49:17 +01:00
4 changed files with 31 additions and 20 deletions

View File

@@ -62,7 +62,11 @@ export const TestIds = {
root: 'properties-panel'
},
node: {
titleInput: 'node-title-input'
titleInput: 'node-title-input',
pinIndicator: 'node-pin-indicator',
innerWrapper: 'node-inner-wrapper',
titleEditorInput: 'title-editor-input',
mainImage: 'main-image'
},
selectionToolbox: {
colorPickerButton: 'color-picker-button',

View File

@@ -1,5 +1,7 @@
import type { Locator } from '@playwright/test'
import { TestIds } from '../selectors'
/** DOM-centric helper for a single Vue-rendered node on the canvas. */
export class VueNodeFixture {
constructor(private readonly locator: Locator) {}
@@ -20,6 +22,10 @@ export class VueNodeFixture {
return this.locator.locator('[data-testid^="node-body-"]')
}
get pinIndicator(): Locator {
return this.locator.getByTestId(TestIds.node.pinIndicator)
}
get collapseButton(): Locator {
return this.locator.locator('[data-testid="node-collapse-button"]')
}

View File

@@ -5,9 +5,9 @@ import {
comfyPageFixture as test
} from '../../../../fixtures/ComfyPage'
import type { ComfyPage } from '../../../../fixtures/ComfyPage'
import { TestIds } from '../../../../fixtures/selectors'
const BYPASS_CLASS = /before:bg-bypass\/60/
const PIN_INDICATOR = '[data-testid="node-pin-indicator"]'
async function clickExactMenuItem(comfyPage: ComfyPage, name: string) {
await comfyPage.page.getByRole('menuitem', { name, exact: true }).click()
@@ -17,10 +17,10 @@ async function clickExactMenuItem(comfyPage: ComfyPage, name: string) {
async function openContextMenu(comfyPage: ComfyPage, nodeTitle: string) {
const header = comfyPage.vueNodes
.getNodeByTitle(nodeTitle)
.locator('.lg-node-header')
.locator('[data-testid^="node-header-"]')
await header.click()
await header.click({ button: 'right' })
const menu = comfyPage.page.locator('.p-contextmenu')
const menu = comfyPage.contextMenu.primeVueMenu
await menu.waitFor({ state: 'visible' })
return menu
}
@@ -37,14 +37,14 @@ async function openMultiNodeContextMenu(
for (const title of titles) {
const header = comfyPage.vueNodes
.getNodeByTitle(title)
.locator('.lg-node-header')
.locator('[data-testid^="node-header-"]')
await header.click({ modifiers: ['ControlOrMeta'] })
}
await comfyPage.nextFrame()
const firstHeader = comfyPage.vueNodes
.getNodeByTitle(titles[0])
.locator('.lg-node-header')
.locator('[data-testid^="node-header-"]')
const box = await firstHeader.boundingBox()
if (!box) throw new Error(`Header for "${titles[0]}" not found`)
await comfyPage.page.mouse.click(
@@ -53,16 +53,15 @@ async function openMultiNodeContextMenu(
{ button: 'right' }
)
const menu = comfyPage.page.locator('.p-contextmenu')
const menu = comfyPage.contextMenu.primeVueMenu
await menu.waitFor({ state: 'visible' })
return menu
}
function getNodeWrapper(comfyPage: ComfyPage, nodeTitle: string): Locator {
return comfyPage.page
.locator('[data-node-id]')
.filter({ hasText: nodeTitle })
.getByTestId('node-inner-wrapper')
return comfyPage.vueNodes
.getNodeByTitle(nodeTitle)
.getByTestId(TestIds.node.innerWrapper)
}
async function getNodeRef(comfyPage: ComfyPage, nodeTitle: string) {
@@ -82,8 +81,8 @@ test.describe('Vue Node Context Menu', () => {
await openContextMenu(comfyPage, 'KSampler')
await clickExactMenuItem(comfyPage, 'Rename')
const titleInput = comfyPage.page.locator(
'.node-title-editor input[type="text"]'
const titleInput = comfyPage.page.getByTestId(
TestIds.node.titleEditorInput
)
await titleInput.waitFor({ state: 'visible' })
await titleInput.fill('My Renamed Sampler')
@@ -137,14 +136,14 @@ test.describe('Vue Node Context Menu', () => {
const pinIndicator = comfyPage.vueNodes
.getNodeByTitle(nodeTitle)
.locator(PIN_INDICATOR)
.getByTestId(TestIds.node.pinIndicator)
await expect(pinIndicator).toBeVisible()
expect(await nodeRef.isPinned()).toBe(true)
// Verify drag blocked
const header = comfyPage.vueNodes
.getNodeByTitle(nodeTitle)
.locator('.lg-node-header')
.locator('[data-testid^="node-header-"]')
const posBeforeDrag = await header.boundingBox()
if (!posBeforeDrag) throw new Error('Header not found')
await comfyPage.canvasOps.dragAndDrop(
@@ -244,7 +243,9 @@ test.describe('Vue Node Context Menu', () => {
comfyPage
}) => {
// Capture the original image src from the node's preview
const imagePreview = comfyPage.page.locator('.image-preview img')
const imagePreview = comfyPage.vueNodes
.getNodeByTitle('Load Image')
.getByTestId(TestIds.node.mainImage)
const originalSrc = await imagePreview.getAttribute('src')
// Write a test image into the browser clipboard
@@ -347,8 +348,7 @@ test.describe('Vue Node Context Menu', () => {
await comfyPage.nodeOps.fillPromptDialog('TestBlueprint')
// Open node library sidebar and search for the blueprint
await comfyPage.page.getByRole('button', { name: 'Node Library' }).click()
await comfyPage.nextFrame()
await comfyPage.menu.nodeLibraryTab.tabButton.click()
const searchBox = comfyPage.page.getByRole('combobox', {
name: 'Search'
})
@@ -416,7 +416,7 @@ test.describe('Vue Node Context Menu', () => {
for (const title of nodeTitles) {
const pinIndicator = comfyPage.vueNodes
.getNodeByTitle(title)
.locator(PIN_INDICATOR)
.getByTestId(TestIds.node.pinIndicator)
await expect(pinIndicator).toBeVisible()
}
@@ -426,7 +426,7 @@ test.describe('Vue Node Context Menu', () => {
for (const title of nodeTitles) {
const pinIndicator = comfyPage.vueNodes
.getNodeByTitle(title)
.locator(PIN_INDICATOR)
.getByTestId(TestIds.node.pinIndicator)
await expect(pinIndicator).not.toBeVisible()
}
})

View File

@@ -7,6 +7,7 @@
<EditableText
:is-editing="showInput"
:model-value="editedTitle"
:input-attrs="{ 'data-testid': 'title-editor-input' }"
@edit="onEdit"
/>
</div>