mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-19 13:59:28 +00:00
test: add Playwright tests for FormDropdown positioning
Amp-Thread-ID: https://ampcode.com/threads/T-019d2285-317d-75db-b838-15f7d9b55b3c Co-authored-by: Amp <amp@ampcode.com>
This commit is contained in:
@@ -100,7 +100,8 @@ export const TestIds = {
|
||||
decrement: 'decrement',
|
||||
increment: 'increment',
|
||||
domWidgetTextarea: 'dom-widget-textarea',
|
||||
subgraphEnterButton: 'subgraph-enter-button'
|
||||
subgraphEnterButton: 'subgraph-enter-button',
|
||||
formDropdownMenu: 'form-dropdown-menu'
|
||||
},
|
||||
builder: {
|
||||
footerNav: 'builder-footer-nav',
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
import {
|
||||
comfyExpect as expect,
|
||||
comfyPageFixture as test
|
||||
} from '../../../../fixtures/ComfyPage'
|
||||
import { TestIds } from '../../../../fixtures/selectors'
|
||||
|
||||
test.describe(
|
||||
'FormDropdown positioning in Vue nodes',
|
||||
{ tag: ['@widget', '@node'] },
|
||||
() => {
|
||||
test.beforeEach(async ({ comfyPage }) => {
|
||||
await comfyPage.settings.setSetting('Comfy.VueNodes.Enabled', true)
|
||||
await comfyPage.workflow.loadWorkflow('widgets/load_image_widget')
|
||||
await comfyPage.vueNodes.waitForNodes()
|
||||
})
|
||||
|
||||
test('dropdown menu appears directly below the trigger', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
const node = comfyPage.vueNodes.getNodeByTitle('Load Image')
|
||||
await expect(node).toBeVisible()
|
||||
|
||||
const trigger = node.locator(
|
||||
'button:has(> span > span), button:has(i.icon-\\[lucide--chevron-down\\])'
|
||||
)
|
||||
await trigger.first().click()
|
||||
|
||||
const menu = comfyPage.page.getByTestId(TestIds.widgets.formDropdownMenu)
|
||||
await expect(menu).toBeVisible({ timeout: 5000 })
|
||||
|
||||
const triggerBox = await trigger.first().boundingBox()
|
||||
const menuBox = await menu.boundingBox()
|
||||
|
||||
expect(triggerBox).toBeTruthy()
|
||||
expect(menuBox).toBeTruthy()
|
||||
|
||||
// Menu top should be near the trigger bottom (within 20px tolerance for padding)
|
||||
expect(menuBox!.y).toBeGreaterThanOrEqual(
|
||||
triggerBox!.y + triggerBox!.height - 5
|
||||
)
|
||||
expect(menuBox!.y).toBeLessThanOrEqual(
|
||||
triggerBox!.y + triggerBox!.height + 20
|
||||
)
|
||||
|
||||
// Menu left should be near the trigger left (within 10px tolerance)
|
||||
expect(menuBox!.x).toBeGreaterThanOrEqual(triggerBox!.x - 10)
|
||||
expect(menuBox!.x).toBeLessThanOrEqual(triggerBox!.x + 10)
|
||||
})
|
||||
|
||||
test('dropdown menu appears correctly at different zoom levels', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
for (const zoom of [0.5, 1.5]) {
|
||||
// Set zoom via canvas
|
||||
await comfyPage.page.evaluate((scale) => {
|
||||
const canvas = window.app!.canvas
|
||||
canvas.ds.scale = scale
|
||||
canvas.setDirty(true, true)
|
||||
}, zoom)
|
||||
await comfyPage.nextFrame()
|
||||
|
||||
const node = comfyPage.vueNodes.getNodeByTitle('Load Image')
|
||||
await expect(node).toBeVisible()
|
||||
|
||||
const trigger = node.locator(
|
||||
'button:has(i.icon-\\[lucide--chevron-down\\])'
|
||||
)
|
||||
await trigger.first().click()
|
||||
|
||||
const menu = comfyPage.page.getByTestId(
|
||||
TestIds.widgets.formDropdownMenu
|
||||
)
|
||||
await expect(menu).toBeVisible({ timeout: 5000 })
|
||||
|
||||
const triggerBox = await trigger.first().boundingBox()
|
||||
const menuBox = await menu.boundingBox()
|
||||
|
||||
expect(triggerBox).toBeTruthy()
|
||||
expect(menuBox).toBeTruthy()
|
||||
|
||||
// Menu top should still be near trigger bottom regardless of zoom
|
||||
expect(menuBox!.y).toBeGreaterThanOrEqual(
|
||||
triggerBox!.y + triggerBox!.height - 5
|
||||
)
|
||||
expect(menuBox!.y).toBeLessThanOrEqual(
|
||||
triggerBox!.y + triggerBox!.height + 20 * zoom
|
||||
)
|
||||
|
||||
// Close dropdown before next iteration
|
||||
await comfyPage.page.keyboard.press('Escape')
|
||||
await expect(menu).not.toBeVisible()
|
||||
}
|
||||
})
|
||||
|
||||
test('dropdown closes on outside click', async ({ comfyPage }) => {
|
||||
const node = comfyPage.vueNodes.getNodeByTitle('Load Image')
|
||||
const trigger = node.locator(
|
||||
'button:has(i.icon-\\[lucide--chevron-down\\])'
|
||||
)
|
||||
await trigger.first().click()
|
||||
|
||||
const menu = comfyPage.page.getByTestId(TestIds.widgets.formDropdownMenu)
|
||||
await expect(menu).toBeVisible({ timeout: 5000 })
|
||||
|
||||
// Click outside the node
|
||||
await comfyPage.page.mouse.click(10, 10)
|
||||
await expect(menu).not.toBeVisible()
|
||||
})
|
||||
|
||||
test('dropdown closes on Escape key', async ({ comfyPage }) => {
|
||||
const node = comfyPage.vueNodes.getNodeByTitle('Load Image')
|
||||
const trigger = node.locator(
|
||||
'button:has(i.icon-\\[lucide--chevron-down\\])'
|
||||
)
|
||||
await trigger.first().click()
|
||||
|
||||
const menu = comfyPage.page.getByTestId(TestIds.widgets.formDropdownMenu)
|
||||
await expect(menu).toBeVisible({ timeout: 5000 })
|
||||
|
||||
await comfyPage.page.keyboard.press('Escape')
|
||||
await expect(menu).not.toBeVisible()
|
||||
})
|
||||
}
|
||||
)
|
||||
@@ -97,6 +97,7 @@ const virtualItems = computed<VirtualDropdownItem[]>(() =>
|
||||
|
||||
<template>
|
||||
<div
|
||||
data-testid="form-dropdown-menu"
|
||||
class="flex h-[640px] w-103 flex-col rounded-lg bg-component-node-background pt-4 outline -outline-offset-1 outline-node-component-border"
|
||||
data-capture-wheel="true"
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user