mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-07-09 16:48:06 +00:00
## Summary - Progressive container-query truncation for the **Keybinding** column in the Settings → Keybinding panel so the **actions column stops getting clipped** at narrow widths. - Tiers (all driven by `@container/keybindings` on the cell content — no JS resize listeners): - `>= 16rem`: `Ctrl S , Ctrl Shift S + 1 more` (2 combos + `N-2 more`) - `>= 12rem`: `Ctrl S + 2 more` (1 combo + `N-1 more`) - `>= 8rem`: `Ctrl S + 2` (1 combo + compact `+N`) - smallest: first combo only - Actions column pinned with `whitespace-nowrap` so it always stays fully visible — the keybindings column compresses first, not the icons. - New `KeybindingList.vue` extracted from `KeybindingPanel.vue`; the prior inline rendering was inlined twice (column body + we'd need it elsewhere if expansion logic changes). - Adds `g.nMoreKeybindingsCompact` and `g.keybindingListAriaLabel` i18n keys (en only; other locales fall back to en). ## Before / after ### before https://github.com/user-attachments/assets/227cdd6a-fd5d-41ba-8af2-2a49e2d9cf15 ### after <img width="936" height="812" alt="Screenshot 2026-05-13 at 11 05 12 AM" src="https://github.com/user-attachments/assets/f750efd8-7d14-4a46-a92d-fa67e57c7909" /> <!-- Drag the PNGs from ~/Desktop/fe-523-screenshots/ into this section. Recommended order: wide → 16rem → 12rem → 8rem, before then after at each tier. Captured by temp/scripts/capture-fe523-screenshots.ts at 4 dialog widths. --> _screenshots pending — drag/drop from `~/Desktop/fe-523-screenshots/`_ The narrow tier shows the original FE-523 bug clearly: on `main` the Source and Actions columns are clipped without a scrollbar; with this PR the keybinding column compresses first, keeping at least the edit icon visible. Some action icons still get clipped at the absolute narrowest tier — full sticky-actions behavior is a follow-up. ## Why draft Manual visual verification of the four container-query tiers on a real backend still needs to happen — the snapshot script runs against a stubbed-out backend, so the dataset is sparse (the multi-combo rows aren't injected). Moving out of draft after that pass. ## References - Fixes [FE-523](https://linear.app/comfyorg/issue/FE-523/update-keybinding-panel-responsiveness) - Figma proposal: https://www.figma.com/design/ZNLRans6JLM3hvCz5LlCQA/Keyboard-Shortcut?node-id=625-17286 - Slack thread: https://comfy-organization.slack.com/archives/C075ANWQ8KS/p1776818615078629?thread_ts=1776808927.654249&cid=C075ANWQ8KS ## Test plan - [x] `pnpm test:unit` — new `KeybindingList.test.ts` covers all 4 tiers (0/1/2/3+ keybindings, count math `N-2` vs `N-1`, container class present, aria-label assembled). - [x] `pnpm typecheck` - [x] `pnpm lint` - [x] Snapshot diff at 4 widths (see Before / after section above once images are attached). - [ ] Manual on a real backend: command with 1 / 2 / 3 / 5 keybindings each renders the correct `+N more` / `+N` value at each tier. - [ ] Manual: confirm whether residual actions-column clipping at the absolute narrowest tier warrants a follow-up.
599 lines
19 KiB
TypeScript
599 lines
19 KiB
TypeScript
import type { Locator, Page } from '@playwright/test'
|
|
import { expect } from '@playwright/test'
|
|
|
|
import type { ComfyPage } from '@e2e/fixtures/ComfyPage'
|
|
import { comfyPageFixture as test } from '@e2e/fixtures/ComfyPage'
|
|
|
|
const MULTI_BINDING_COMMAND = 'Comfy.Canvas.DeleteSelectedItems'
|
|
const SINGLE_BINDING_COMMAND = 'Comfy.SaveWorkflow'
|
|
const NO_BINDING_COMMAND = 'TestCommand.KeybindingPanelE2E.NoBinding'
|
|
|
|
async function searchKeybindings(page: Page, query: string) {
|
|
await getKeybindingSearchInput(page).fill(query)
|
|
}
|
|
|
|
async function clearSearch(page: Page) {
|
|
await getKeybindingSearchInput(page).clear()
|
|
}
|
|
|
|
function getKeybindingSearchInput(page: Page): Locator {
|
|
return page.getByPlaceholder('Search Keybindings...')
|
|
}
|
|
|
|
function getCommandRow(page: Page, commandId: string): Locator {
|
|
return page
|
|
.locator('.keybinding-panel tr')
|
|
.filter({ has: page.locator(`[title="${commandId}"]`) })
|
|
}
|
|
|
|
function getExpansionContent(page: Page, commandId: string): Locator {
|
|
// PrimeVue renders the expansion row as the next sibling <tr> of the
|
|
// expanded row. Scoping by sibling avoids matching unrelated expanded rows.
|
|
return getCommandRow(page, commandId)
|
|
.locator('xpath=following-sibling::tr[1]')
|
|
.getByTestId('keybinding-expansion-content')
|
|
}
|
|
|
|
async function openContextMenu(page: Page, commandId: string) {
|
|
const row = getCommandRow(page, commandId)
|
|
await row.locator(`[title="${commandId}"]`).click({ button: 'right' })
|
|
await expect(
|
|
page.getByRole('menuitem', { name: /Change keybinding/i })
|
|
).toBeVisible()
|
|
}
|
|
|
|
function getKeybindingInput(page: Page): Locator {
|
|
return getEditKeybindingDialog(page).locator('input[autofocus]')
|
|
}
|
|
|
|
function getEditKeybindingDialog(page: Page): Locator {
|
|
return page.getByRole('dialog', { name: /Modify keybinding/i })
|
|
}
|
|
|
|
function getRemoveAllKeybindingsDialog(page: Page): Locator {
|
|
return page.getByRole('dialog', { name: /Remove all keybindings/i })
|
|
}
|
|
|
|
function getResetAllKeybindingsDialog(page: Page): Locator {
|
|
return page.getByRole('dialog', { name: /Reset all keybindings/i })
|
|
}
|
|
|
|
async function pressComboOnInput(page: Page, combo: string) {
|
|
const input = getKeybindingInput(page)
|
|
await expect(input).toBeFocused()
|
|
await input.press(combo)
|
|
}
|
|
|
|
async function saveAndCloseKeybindingDialog(page: Page) {
|
|
const dialog = getEditKeybindingDialog(page)
|
|
await dialog.getByRole('button', { name: /Save/i }).click()
|
|
await expect(dialog).toBeHidden()
|
|
}
|
|
|
|
async function cancelAndCloseDialog(page: Page) {
|
|
const dialog = getEditKeybindingDialog(page)
|
|
await dialog.getByRole('button', { name: /Cancel/i }).click()
|
|
await expect(dialog).toBeHidden()
|
|
}
|
|
|
|
async function addKeybindingToRow(page: Page, row: Locator, combo: string) {
|
|
await row.getByRole('button', { name: /Add new keybinding/i }).click()
|
|
await pressComboOnInput(page, combo)
|
|
await saveAndCloseKeybindingDialog(page)
|
|
}
|
|
|
|
test.beforeEach(async ({ comfyPage }) => {
|
|
await registerNoBindingCommand(comfyPage)
|
|
await comfyPage.settingDialog.open()
|
|
await comfyPage.settingDialog.category('Keybinding').click()
|
|
})
|
|
|
|
test.afterEach(async ({ comfyPage }) => {
|
|
await comfyPage.settings.setSetting('Comfy.Keybinding.NewBindings', [])
|
|
await comfyPage.settings.setSetting('Comfy.Keybinding.UnsetBindings', [])
|
|
})
|
|
|
|
async function registerNoBindingCommand(comfyPage: ComfyPage) {
|
|
await comfyPage.page.evaluate((commandId) => {
|
|
const app = window.app!
|
|
app.registerExtension({
|
|
name: 'TestExtension.KeybindingPanelE2E',
|
|
commands: [{ id: commandId, function: () => {} }]
|
|
})
|
|
}, NO_BINDING_COMMAND)
|
|
}
|
|
|
|
test.describe('Keybinding Panel', { tag: '@keyboard' }, () => {
|
|
test.describe('Row Expansion', () => {
|
|
test('Click on row with 2+ keybindings toggles expansion', async ({
|
|
comfyPage
|
|
}) => {
|
|
const { page } = comfyPage
|
|
|
|
await searchKeybindings(page, MULTI_BINDING_COMMAND)
|
|
const row = getCommandRow(page, MULTI_BINDING_COMMAND)
|
|
await expect(row).toBeVisible()
|
|
|
|
await row.locator(`[title="${MULTI_BINDING_COMMAND}"]`).click()
|
|
|
|
const expansionContent = getExpansionContent(page, MULTI_BINDING_COMMAND)
|
|
await expect(expansionContent).toBeVisible()
|
|
|
|
await row.locator(`[title="${MULTI_BINDING_COMMAND}"]`).click()
|
|
await expect(expansionContent).toBeHidden()
|
|
})
|
|
|
|
test('Click on row with 1 keybinding does not expand', async ({
|
|
comfyPage
|
|
}) => {
|
|
const { page } = comfyPage
|
|
|
|
await searchKeybindings(page, SINGLE_BINDING_COMMAND)
|
|
const row = getCommandRow(page, SINGLE_BINDING_COMMAND)
|
|
await expect(row).toBeVisible()
|
|
|
|
await row.locator(`[title="${SINGLE_BINDING_COMMAND}"]`).click()
|
|
|
|
const expansionContent = getExpansionContent(page, SINGLE_BINDING_COMMAND)
|
|
await expect(expansionContent).toBeHidden()
|
|
})
|
|
})
|
|
|
|
test.describe('Double-Click', () => {
|
|
test('Double-click row with 0 keybindings opens Add dialog', async ({
|
|
comfyPage
|
|
}) => {
|
|
const { page } = comfyPage
|
|
|
|
await searchKeybindings(page, NO_BINDING_COMMAND)
|
|
const row = getCommandRow(page, NO_BINDING_COMMAND)
|
|
await expect(row).toBeVisible()
|
|
|
|
await row.locator(`[title="${NO_BINDING_COMMAND}"]`).dblclick()
|
|
|
|
const input = getKeybindingInput(page)
|
|
await expect(input).toBeVisible()
|
|
|
|
await cancelAndCloseDialog(page)
|
|
})
|
|
|
|
test('Double-click row with 1 keybinding opens Edit dialog', async ({
|
|
comfyPage
|
|
}) => {
|
|
const { page } = comfyPage
|
|
|
|
await searchKeybindings(page, SINGLE_BINDING_COMMAND)
|
|
const row = getCommandRow(page, SINGLE_BINDING_COMMAND)
|
|
await expect(row).toBeVisible()
|
|
|
|
await row.locator(`[title="${SINGLE_BINDING_COMMAND}"]`).dblclick()
|
|
|
|
const input = getKeybindingInput(page)
|
|
await expect(input).toBeVisible()
|
|
|
|
await cancelAndCloseDialog(page)
|
|
})
|
|
})
|
|
|
|
test.describe('Context Menu', () => {
|
|
test('Right-click row shows context menu with correct items', async ({
|
|
comfyPage
|
|
}) => {
|
|
const { page } = comfyPage
|
|
|
|
await searchKeybindings(page, SINGLE_BINDING_COMMAND)
|
|
await openContextMenu(page, SINGLE_BINDING_COMMAND)
|
|
|
|
const changeItem = page.getByRole('menuitem', {
|
|
name: /Change keybinding/i
|
|
})
|
|
const addItem = page.getByRole('menuitem', {
|
|
name: /Add new keybinding/i
|
|
})
|
|
const resetItem = page.getByRole('menuitem', {
|
|
name: /Reset to default/i
|
|
})
|
|
const removeItem = page.getByRole('menuitem', {
|
|
name: /Remove keybinding/i
|
|
})
|
|
|
|
await expect(changeItem).toBeVisible()
|
|
await expect(addItem).toBeVisible()
|
|
await expect(resetItem).toBeVisible()
|
|
await expect(removeItem).toBeVisible()
|
|
|
|
await page.keyboard.press('Escape')
|
|
})
|
|
|
|
test("Context menu 'Add new keybinding' opens add dialog", async ({
|
|
comfyPage
|
|
}) => {
|
|
const { page } = comfyPage
|
|
|
|
await searchKeybindings(page, SINGLE_BINDING_COMMAND)
|
|
await openContextMenu(page, SINGLE_BINDING_COMMAND)
|
|
|
|
await page.getByRole('menuitem', { name: /Add new keybinding/i }).click()
|
|
|
|
const input = getKeybindingInput(page)
|
|
await expect(input).toBeVisible()
|
|
|
|
await cancelAndCloseDialog(page)
|
|
})
|
|
|
|
test("Context menu 'Change keybinding' on single-binding command opens edit dialog", async ({
|
|
comfyPage
|
|
}) => {
|
|
const { page } = comfyPage
|
|
|
|
await searchKeybindings(page, SINGLE_BINDING_COMMAND)
|
|
await openContextMenu(page, SINGLE_BINDING_COMMAND)
|
|
|
|
await page.getByRole('menuitem', { name: /Change keybinding/i }).click()
|
|
|
|
const input = getKeybindingInput(page)
|
|
await expect(input).toBeVisible()
|
|
|
|
await cancelAndCloseDialog(page)
|
|
})
|
|
|
|
test("Context menu 'Change keybinding' on multi-binding command expands row", async ({
|
|
comfyPage
|
|
}) => {
|
|
const { page } = comfyPage
|
|
|
|
await searchKeybindings(page, MULTI_BINDING_COMMAND)
|
|
|
|
const expansionContent = getExpansionContent(page, MULTI_BINDING_COMMAND)
|
|
await expect(expansionContent).toBeHidden()
|
|
|
|
await openContextMenu(page, MULTI_BINDING_COMMAND)
|
|
|
|
await page.getByRole('menuitem', { name: /Change keybinding/i }).click()
|
|
|
|
await expect(expansionContent).toBeVisible()
|
|
})
|
|
|
|
test("Context menu 'Remove keybinding' after adding second binding shows confirm dialog", async ({
|
|
comfyPage
|
|
}) => {
|
|
const { page } = comfyPage
|
|
|
|
await searchKeybindings(page, SINGLE_BINDING_COMMAND)
|
|
const row = getCommandRow(page, SINGLE_BINDING_COMMAND)
|
|
|
|
await addKeybindingToRow(page, row, 'Control+Shift+F9')
|
|
|
|
await openContextMenu(page, SINGLE_BINDING_COMMAND)
|
|
await page.getByRole('menuitem', { name: /Remove keybinding/i }).click()
|
|
|
|
const confirmDialog = getRemoveAllKeybindingsDialog(page)
|
|
await expect(confirmDialog).toBeVisible()
|
|
await confirmDialog.getByRole('button', { name: /Remove all/i }).click()
|
|
|
|
await expect(row.locator('td').nth(1)).toContainText('-')
|
|
})
|
|
|
|
test("Context menu 'Reset to default' resets modified command", async ({
|
|
comfyPage
|
|
}) => {
|
|
const { page } = comfyPage
|
|
|
|
await searchKeybindings(page, SINGLE_BINDING_COMMAND)
|
|
const row = getCommandRow(page, SINGLE_BINDING_COMMAND)
|
|
|
|
await addKeybindingToRow(page, row, 'Control+Shift+F10')
|
|
|
|
await openContextMenu(page, SINGLE_BINDING_COMMAND)
|
|
await page.getByRole('menuitem', { name: /Reset to default/i }).click()
|
|
|
|
await expect(row.getByRole('button', { name: /Reset/i })).toBeDisabled()
|
|
})
|
|
|
|
test('Context menu items disabled when no keybindings', async ({
|
|
comfyPage
|
|
}) => {
|
|
const { page } = comfyPage
|
|
|
|
await searchKeybindings(page, NO_BINDING_COMMAND)
|
|
await openContextMenu(page, NO_BINDING_COMMAND)
|
|
|
|
const changeItem = page.getByRole('menuitem', {
|
|
name: /Change keybinding/i
|
|
})
|
|
const removeItem = page.getByRole('menuitem', {
|
|
name: /Remove keybinding/i
|
|
})
|
|
|
|
await expect(changeItem).toHaveAttribute('data-disabled', '')
|
|
await expect(removeItem).toHaveAttribute('data-disabled', '')
|
|
|
|
await page.keyboard.press('Escape')
|
|
})
|
|
})
|
|
|
|
test.describe('Action Buttons', () => {
|
|
test('Edit button opens edit dialog for single-binding command', async ({
|
|
comfyPage
|
|
}) => {
|
|
const { page } = comfyPage
|
|
|
|
await searchKeybindings(page, SINGLE_BINDING_COMMAND)
|
|
const row = getCommandRow(page, SINGLE_BINDING_COMMAND)
|
|
|
|
const editButton = row.getByRole('button', { name: /^Edit$/i })
|
|
await expect(editButton).toBeVisible()
|
|
await editButton.click()
|
|
|
|
const input = getKeybindingInput(page)
|
|
await expect(input).toBeVisible()
|
|
|
|
await cancelAndCloseDialog(page)
|
|
})
|
|
|
|
test('Add button opens add dialog', async ({ comfyPage }) => {
|
|
const { page } = comfyPage
|
|
|
|
await searchKeybindings(page, SINGLE_BINDING_COMMAND)
|
|
const row = getCommandRow(page, SINGLE_BINDING_COMMAND)
|
|
|
|
await row.getByRole('button', { name: /Add new keybinding/i }).click()
|
|
|
|
const input = getKeybindingInput(page)
|
|
await expect(input).toBeVisible()
|
|
|
|
await cancelAndCloseDialog(page)
|
|
})
|
|
|
|
test('Reset button is disabled for unmodified commands', async ({
|
|
comfyPage
|
|
}) => {
|
|
const { page } = comfyPage
|
|
|
|
await searchKeybindings(page, SINGLE_BINDING_COMMAND)
|
|
const row = getCommandRow(page, SINGLE_BINDING_COMMAND)
|
|
|
|
const resetButton = row.getByRole('button', { name: /Reset/i })
|
|
await expect(resetButton).toBeDisabled()
|
|
})
|
|
|
|
test('Reset button resets modified keybinding', async ({ comfyPage }) => {
|
|
const { page } = comfyPage
|
|
|
|
await searchKeybindings(page, SINGLE_BINDING_COMMAND)
|
|
const row = getCommandRow(page, SINGLE_BINDING_COMMAND)
|
|
|
|
await addKeybindingToRow(page, row, 'Control+Shift+F11')
|
|
|
|
const resetButton = row.getByRole('button', { name: /Reset/i })
|
|
await expect(resetButton).toBeEnabled()
|
|
|
|
await resetButton.click()
|
|
|
|
await expect(resetButton).toBeDisabled()
|
|
})
|
|
|
|
test('Delete button is disabled for commands with 0 keybindings', async ({
|
|
comfyPage
|
|
}) => {
|
|
const { page } = comfyPage
|
|
|
|
await searchKeybindings(page, NO_BINDING_COMMAND)
|
|
const row = getCommandRow(page, NO_BINDING_COMMAND)
|
|
|
|
const deleteButton = row.getByRole('button', { name: /Delete/i })
|
|
await expect(deleteButton).toBeDisabled()
|
|
})
|
|
|
|
test('Delete button removes single keybinding directly', async ({
|
|
comfyPage
|
|
}) => {
|
|
const { page } = comfyPage
|
|
|
|
await searchKeybindings(page, NO_BINDING_COMMAND)
|
|
const row = getCommandRow(page, NO_BINDING_COMMAND)
|
|
|
|
await addKeybindingToRow(page, row, 'Control+Shift+F12')
|
|
|
|
const deleteButton = row.getByRole('button', { name: /Delete/i })
|
|
await expect(deleteButton).toBeEnabled()
|
|
await deleteButton.click()
|
|
|
|
await expect(row.locator('td').nth(1)).toContainText('-')
|
|
})
|
|
|
|
test('Delete button on command with 2+ keybindings shows confirm dialog', async ({
|
|
comfyPage
|
|
}) => {
|
|
const { page } = comfyPage
|
|
|
|
await searchKeybindings(page, MULTI_BINDING_COMMAND)
|
|
const row = getCommandRow(page, MULTI_BINDING_COMMAND)
|
|
|
|
const deleteButton = row.getByRole('button', { name: /Delete/i })
|
|
await deleteButton.click()
|
|
|
|
const confirmDialog = getRemoveAllKeybindingsDialog(page)
|
|
await expect(confirmDialog).toBeVisible()
|
|
|
|
await confirmDialog.getByRole('button', { name: /Cancel/i }).click()
|
|
await expect(confirmDialog).toBeHidden()
|
|
await expect(row.locator('td').nth(1)).not.toContainText('-')
|
|
})
|
|
})
|
|
|
|
test.describe('Expanded Row Actions', () => {
|
|
test('Edit button in expanded row opens edit dialog for that binding', async ({
|
|
comfyPage
|
|
}) => {
|
|
const { page } = comfyPage
|
|
|
|
await searchKeybindings(page, MULTI_BINDING_COMMAND)
|
|
|
|
await page.locator(`[title="${MULTI_BINDING_COMMAND}"]`).click()
|
|
const expansionContent = getExpansionContent(page, MULTI_BINDING_COMMAND)
|
|
await expect(expansionContent).toBeVisible()
|
|
|
|
const firstBindingRow = expansionContent
|
|
.getByTestId('keybinding-expansion-binding')
|
|
.first()
|
|
await firstBindingRow.getByRole('button', { name: /^Edit$/i }).click()
|
|
|
|
const input = getKeybindingInput(page)
|
|
await expect(input).toBeVisible()
|
|
|
|
await cancelAndCloseDialog(page)
|
|
})
|
|
|
|
test('Delete button in expanded row removes that binding and collapses', async ({
|
|
comfyPage
|
|
}) => {
|
|
const { page } = comfyPage
|
|
|
|
await searchKeybindings(page, MULTI_BINDING_COMMAND)
|
|
|
|
await page.locator(`[title="${MULTI_BINDING_COMMAND}"]`).click()
|
|
const expansionContent = getExpansionContent(page, MULTI_BINDING_COMMAND)
|
|
await expect(expansionContent).toBeVisible()
|
|
|
|
const bindingRows = expansionContent.getByTestId(
|
|
'keybinding-expansion-binding'
|
|
)
|
|
await expect
|
|
.poll(() => bindingRows.count(), {
|
|
message: 'Expected at least 2 bindings'
|
|
})
|
|
.toBeGreaterThanOrEqual(2)
|
|
const initialBindingCount = await bindingRows.count()
|
|
|
|
await bindingRows
|
|
.first()
|
|
.getByRole('button', { name: /Remove keybinding/i })
|
|
.click()
|
|
|
|
if (initialBindingCount === 2) {
|
|
// Expansion auto-collapses when bindings drop below 2
|
|
await expect(expansionContent).toBeHidden()
|
|
} else {
|
|
await expect(bindingRows).toHaveCount(initialBindingCount - 1)
|
|
}
|
|
})
|
|
})
|
|
|
|
test.describe('Reset All', () => {
|
|
test('Reset All button shows confirmation and resets on confirm', async ({
|
|
comfyPage
|
|
}) => {
|
|
const { page } = comfyPage
|
|
|
|
await searchKeybindings(page, SINGLE_BINDING_COMMAND)
|
|
const row = getCommandRow(page, SINGLE_BINDING_COMMAND)
|
|
await addKeybindingToRow(page, row, 'Control+Shift+F8')
|
|
|
|
await expect(row.getByRole('button', { name: /Reset/i })).toBeEnabled()
|
|
|
|
await clearSearch(page)
|
|
|
|
const resetAllButton = page
|
|
.locator('.keybinding-panel')
|
|
.getByRole('button', { name: /Reset All/i })
|
|
await resetAllButton.click()
|
|
|
|
const confirmDialog = getResetAllKeybindingsDialog(page)
|
|
await expect(confirmDialog).toBeVisible()
|
|
await expect(confirmDialog).toContainText(/Reset all keybindings/i)
|
|
|
|
await confirmDialog.getByRole('button', { name: /Reset All/i }).click()
|
|
|
|
await expect(comfyPage.toast.visibleToasts).toHaveCount(1)
|
|
|
|
await searchKeybindings(page, SINGLE_BINDING_COMMAND)
|
|
const rowAfterReset = getCommandRow(page, SINGLE_BINDING_COMMAND)
|
|
await expect(
|
|
rowAfterReset.getByRole('button', { name: /Reset/i })
|
|
).toBeDisabled()
|
|
})
|
|
|
|
test('Reset All confirmation can be cancelled', async ({ comfyPage }) => {
|
|
const { page } = comfyPage
|
|
|
|
const resetAllButton = page
|
|
.locator('.keybinding-panel')
|
|
.getByRole('button', { name: /Reset All/i })
|
|
await resetAllButton.click()
|
|
|
|
const confirmDialog = getResetAllKeybindingsDialog(page)
|
|
await expect(confirmDialog).toBeVisible()
|
|
await confirmDialog.getByRole('button', { name: /Cancel/i }).click()
|
|
|
|
await expect(confirmDialog).toBeHidden()
|
|
})
|
|
})
|
|
|
|
test.describe('Search Filter', () => {
|
|
test('Typing in search clears expanded rows', async ({ comfyPage }) => {
|
|
const { page } = comfyPage
|
|
|
|
await searchKeybindings(page, MULTI_BINDING_COMMAND)
|
|
|
|
await page.locator(`[title="${MULTI_BINDING_COMMAND}"]`).click()
|
|
const expansionContent = getExpansionContent(page, MULTI_BINDING_COMMAND)
|
|
await expect(expansionContent).toBeVisible()
|
|
|
|
// Changing the filter triggers watch(filters, ...) which clears expansion
|
|
await searchKeybindings(page, MULTI_BINDING_COMMAND + ' ')
|
|
await expect(expansionContent).toBeHidden()
|
|
})
|
|
})
|
|
|
|
test.describe('Responsive Layout', () => {
|
|
test('Action buttons stay on screen without horizontal scroll at narrow widths', async ({
|
|
comfyPage
|
|
}) => {
|
|
const { page } = comfyPage
|
|
|
|
await searchKeybindings(page, MULTI_BINDING_COMMAND)
|
|
const row = getCommandRow(page, MULTI_BINDING_COMMAND)
|
|
await expect(row).toBeVisible()
|
|
|
|
await page.setViewportSize({ width: 480, height: 800 })
|
|
|
|
await expect(
|
|
row.getByRole('button', { name: /Delete/i })
|
|
).toBeInViewport()
|
|
await expect(
|
|
row.getByRole('button', { name: /Add new keybinding/i })
|
|
).toBeInViewport()
|
|
|
|
const hasHorizontalScroll = await page
|
|
.locator('.keybinding-panel .p-datatable-table-container')
|
|
.evaluate((el) => el.scrollWidth > el.clientWidth + 1)
|
|
expect(hasHorizontalScroll).toBe(false)
|
|
})
|
|
|
|
test('Keybinding column compresses with width while actions stay reachable', async ({
|
|
comfyPage
|
|
}) => {
|
|
const { page } = comfyPage
|
|
|
|
await searchKeybindings(page, MULTI_BINDING_COMMAND)
|
|
const row = getCommandRow(page, MULTI_BINDING_COMMAND)
|
|
const keybindingList = row.getByTestId('keybinding-list')
|
|
await expect(keybindingList).toBeVisible()
|
|
|
|
const listWidthAt = async (viewportWidth: number) => {
|
|
await page.setViewportSize({ width: viewportWidth, height: 800 })
|
|
return keybindingList.evaluate((el) => el.getBoundingClientRect().width)
|
|
}
|
|
|
|
const wideWidth = await listWidthAt(1280)
|
|
const narrowWidth = await listWidthAt(560)
|
|
|
|
expect(narrowWidth).toBeLessThan(wideWidth)
|
|
await expect(
|
|
row.getByRole('button', { name: /Delete/i })
|
|
).toBeInViewport()
|
|
})
|
|
})
|
|
})
|