mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-20 14:30:41 +00:00
## Summary Complete the @e2e/ path alias migration started in #10735 by converting all 354 remaining relative imports and adding a lint rule to prevent backsliding. ## Changes - **What**: Migrate all relative imports in browser_tests/ to use `@e2e/` (intra-directory) and `@/` (src/ imports) path aliases. Add `no-restricted-imports` ESLint rule banning `./` and `../` imports in `browser_tests/**/*.ts`. Suppress pre-existing oxlint `no-eval` and `no-console` warnings exposed by touching those files. ## Review Focus - ESLint flat-config merging: the `@playwright/test` ban and relative-import ban are in two separate blocks to avoid last-match-wins collision with the `useI18n`/`useVirtualList` blocks higher in the config. - The `['./**', '../**']` glob patterns (not `['./*', '../*']`) are needed to catch multi-level relative paths like `../../../src/foo`. Follows up on #10735 ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-10958-test-migrate-browser_tests-to-e2e-path-alias-and-add-lint-rule-33c6d73d365081649d1be771eac986fd) by [Unito](https://www.unito.io) Co-authored-by: Amp <amp@ampcode.com>
258 lines
8.3 KiB
TypeScript
258 lines
8.3 KiB
TypeScript
import fs from 'node:fs'
|
|
import os from 'node:os'
|
|
import path from 'node:path'
|
|
|
|
import type { Page } from '@playwright/test'
|
|
import { expect } from '@playwright/test'
|
|
|
|
import { comfyPageFixture as test } from '@e2e/fixtures/ComfyPage'
|
|
|
|
const TEST_PRESET = {
|
|
name: 'test-preset',
|
|
newBindings: [
|
|
{
|
|
commandId: 'Comfy.Canvas.SelectAll',
|
|
combo: { key: 'a', ctrl: true, shift: true },
|
|
targetElementId: 'graph-canvas-container'
|
|
}
|
|
],
|
|
unsetBindings: [
|
|
{
|
|
commandId: 'Comfy.Canvas.SelectAll',
|
|
combo: { key: 'a', ctrl: true },
|
|
targetElementId: 'graph-canvas-container'
|
|
}
|
|
]
|
|
}
|
|
|
|
async function importPreset(page: Page, preset: typeof TEST_PRESET) {
|
|
const menuButton = page.getByTestId('keybinding-preset-menu')
|
|
await menuButton.click()
|
|
|
|
const fileChooserPromise = page.waitForEvent('filechooser')
|
|
await page.getByRole('menuitem', { name: /Import preset/i }).click()
|
|
const fileChooser = await fileChooserPromise
|
|
|
|
const presetPath = path.join(os.tmpdir(), 'test-preset.json')
|
|
fs.writeFileSync(presetPath, JSON.stringify(preset))
|
|
await fileChooser.setFiles(presetPath)
|
|
}
|
|
|
|
test.beforeEach(async ({ comfyPage }) => {
|
|
await comfyPage.settings.setSetting('Comfy.UseNewMenu', 'Disabled')
|
|
})
|
|
|
|
test.afterEach(async ({ comfyPage }) => {
|
|
await comfyPage.request.fetch(
|
|
`${comfyPage.url}/api/userdata/keybindings%2Ftest-preset.json`,
|
|
{ method: 'DELETE' }
|
|
)
|
|
await comfyPage.settings.setSetting(
|
|
'Comfy.Keybinding.CurrentPreset',
|
|
'default'
|
|
)
|
|
})
|
|
|
|
test.describe('Keybinding Presets', { tag: '@keyboard' }, () => {
|
|
test('Can import a preset, use remapped keybinding, and switch back to default', async ({
|
|
comfyPage
|
|
}) => {
|
|
test.setTimeout(30000)
|
|
const { page } = comfyPage
|
|
|
|
// Verify default Ctrl+A select-all works
|
|
await comfyPage.workflow.loadWorkflow('default')
|
|
await comfyPage.canvas.press('Control+a')
|
|
await comfyPage.canvas.press('Delete')
|
|
await expect.poll(() => comfyPage.nodeOps.getGraphNodesCount()).toBe(0)
|
|
|
|
// Open keybinding settings panel
|
|
await comfyPage.settingDialog.open()
|
|
await comfyPage.settingDialog.category('Keybinding').click()
|
|
|
|
await importPreset(page, TEST_PRESET)
|
|
|
|
// Verify active preset switched to test-preset
|
|
const presetTrigger = page
|
|
.locator('#keybinding-panel-actions')
|
|
.locator('button[role="combobox"]')
|
|
await expect(presetTrigger).toContainText('test-preset')
|
|
|
|
// Wait for toast to auto-dismiss, then close settings via Escape
|
|
await expect(comfyPage.toast.visibleToasts).toHaveCount(0, {
|
|
timeout: 5000
|
|
})
|
|
await page.keyboard.press('Escape')
|
|
await comfyPage.settingDialog.waitForHidden()
|
|
|
|
// Load workflow again, use new keybind Ctrl+Shift+A
|
|
await comfyPage.workflow.loadWorkflow('default')
|
|
await comfyPage.canvas.press('Control+Shift+a')
|
|
await expect
|
|
.poll(() => comfyPage.nodeOps.getSelectedGraphNodesCount())
|
|
.toBeGreaterThan(0)
|
|
await comfyPage.canvas.press('Delete')
|
|
await expect.poll(() => comfyPage.nodeOps.getGraphNodesCount()).toBe(0)
|
|
|
|
// Switch back to default preset
|
|
await comfyPage.settingDialog.open()
|
|
await comfyPage.settingDialog.category('Keybinding').click()
|
|
|
|
await presetTrigger.click()
|
|
await page.getByRole('option', { name: /Default Preset/i }).click()
|
|
|
|
// Handle unsaved changes dialog if the preset was marked as modified
|
|
const discardButton = page.getByRole('button', {
|
|
name: /Discard and Switch/i
|
|
})
|
|
if (await discardButton.isVisible({ timeout: 2000 }).catch(() => false)) {
|
|
await discardButton.click()
|
|
}
|
|
|
|
await expect(presetTrigger).toContainText('Default Preset')
|
|
|
|
await page.keyboard.press('Escape')
|
|
await comfyPage.settingDialog.waitForHidden()
|
|
})
|
|
|
|
test('Can export a preset and re-import it', async ({ comfyPage }) => {
|
|
test.setTimeout(30000)
|
|
const { page } = comfyPage
|
|
const menuButton = page.getByTestId('keybinding-preset-menu')
|
|
|
|
// Open keybinding settings panel
|
|
await comfyPage.settingDialog.open()
|
|
await comfyPage.settingDialog.category('Keybinding').click()
|
|
|
|
await importPreset(page, TEST_PRESET)
|
|
|
|
// Verify active preset switched to test-preset
|
|
const presetTrigger = page
|
|
.locator('#keybinding-panel-actions')
|
|
.locator('button[role="combobox"]')
|
|
await expect(presetTrigger).toContainText('test-preset')
|
|
|
|
// Wait for toast to auto-dismiss
|
|
await expect(comfyPage.toast.visibleToasts).toHaveCount(0, {
|
|
timeout: 5000
|
|
})
|
|
|
|
// Export via ellipsis menu
|
|
await menuButton.click()
|
|
const downloadPromise = page.waitForEvent('download')
|
|
await page.getByRole('menuitem', { name: /Export preset/i }).click()
|
|
const download = await downloadPromise
|
|
|
|
// Verify filename contains test-preset
|
|
expect(download.suggestedFilename()).toContain('test-preset')
|
|
|
|
// Close settings
|
|
await page.keyboard.press('Escape')
|
|
await comfyPage.settingDialog.waitForHidden()
|
|
|
|
// Verify the downloaded file is valid JSON with correct structure
|
|
const downloadPath = await download.path()
|
|
expect(downloadPath).toBeTruthy()
|
|
const content = fs.readFileSync(downloadPath!, 'utf-8')
|
|
const parsed = JSON.parse(content) as {
|
|
name: string
|
|
newBindings: unknown[]
|
|
unsetBindings: unknown[]
|
|
}
|
|
expect(parsed).toHaveProperty('name')
|
|
expect(parsed).toHaveProperty('newBindings')
|
|
expect(parsed).toHaveProperty('unsetBindings')
|
|
expect(parsed.name).toBe('test-preset')
|
|
})
|
|
|
|
test('Can delete an imported preset', async ({ comfyPage }) => {
|
|
test.setTimeout(30000)
|
|
const { page } = comfyPage
|
|
const menuButton = page.getByTestId('keybinding-preset-menu')
|
|
|
|
// Open keybinding settings panel
|
|
await comfyPage.settingDialog.open()
|
|
await comfyPage.settingDialog.category('Keybinding').click()
|
|
|
|
await importPreset(page, TEST_PRESET)
|
|
|
|
// Verify active preset switched to test-preset
|
|
const presetTrigger = page
|
|
.locator('#keybinding-panel-actions')
|
|
.locator('button[role="combobox"]')
|
|
await expect(presetTrigger).toContainText('test-preset')
|
|
|
|
// Wait for toast to auto-dismiss
|
|
await expect(comfyPage.toast.visibleToasts).toHaveCount(0, {
|
|
timeout: 5000
|
|
})
|
|
|
|
// Delete via ellipsis menu
|
|
await menuButton.click()
|
|
await page.getByRole('menuitem', { name: /Delete preset/i }).click()
|
|
|
|
// Confirm deletion in the dialog
|
|
const confirmDialog = page.getByRole('dialog', {
|
|
name: /Delete the current preset/i
|
|
})
|
|
await confirmDialog.getByRole('button', { name: /Delete/i }).click()
|
|
|
|
// Verify preset trigger now shows Default Preset
|
|
await expect(presetTrigger).toContainText('Default Preset')
|
|
|
|
// Close settings
|
|
await page.keyboard.press('Escape')
|
|
await comfyPage.settingDialog.waitForHidden()
|
|
})
|
|
|
|
test('Can save modifications as a new preset', async ({ comfyPage }) => {
|
|
test.setTimeout(30000)
|
|
const { page } = comfyPage
|
|
const menuButton = page.getByTestId('keybinding-preset-menu')
|
|
|
|
// Open keybinding settings panel
|
|
await comfyPage.settingDialog.open()
|
|
await comfyPage.settingDialog.category('Keybinding').click()
|
|
|
|
await importPreset(page, TEST_PRESET)
|
|
|
|
// Verify active preset switched to test-preset
|
|
const presetTrigger = page
|
|
.locator('#keybinding-panel-actions')
|
|
.locator('button[role="combobox"]')
|
|
await expect(presetTrigger).toContainText('test-preset')
|
|
|
|
// Wait for toast to auto-dismiss
|
|
await expect(comfyPage.toast.visibleToasts).toHaveCount(0, {
|
|
timeout: 5000
|
|
})
|
|
|
|
// Save as new preset via ellipsis menu
|
|
await menuButton.click()
|
|
await page.getByRole('menuitem', { name: /Save as new preset/i }).click()
|
|
|
|
// Fill in the preset name in the prompt dialog
|
|
const promptInput = page.locator('.prompt-dialog-content input')
|
|
await promptInput.fill('my-custom-preset')
|
|
await promptInput.press('Enter')
|
|
|
|
// Wait for toast to auto-dismiss
|
|
await expect(comfyPage.toast.visibleToasts).toHaveCount(0, {
|
|
timeout: 5000
|
|
})
|
|
|
|
// Verify preset trigger shows my-custom-preset
|
|
await expect(presetTrigger).toContainText('my-custom-preset')
|
|
|
|
// Close settings
|
|
await page.keyboard.press('Escape')
|
|
await comfyPage.settingDialog.waitForHidden()
|
|
|
|
// Cleanup: delete the my-custom-preset file
|
|
await comfyPage.request.fetch(
|
|
`${comfyPage.url}/api/userdata/keybindings%2Fmy-custom-preset.json`,
|
|
{ method: 'DELETE' }
|
|
)
|
|
})
|
|
})
|