mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-20 06:20:11 +00:00
## Summary Alright, alright, alright. These e2e tests have been runnin' around like they're late for somethin', settin' tight little timeouts like the world's gonna end in 250 milliseconds. Man, you gotta *breathe*. Let the framework do its thing. Go slow to go fast, that's what I always say. ## Changes - **What**: Removed ~120 redundant timeout overrides from auto-retrying Playwright assertions (`toBeVisible`, `toBeHidden`, `toHaveCount`, `toBeEnabled`, `toHaveAttribute`, `toContainText`, `expect.poll`) where 5000ms is already the default. Also removed sub-5s timeouts (1s, 2s, 3s) that were just *begging* for flaky failures — like wearin' a belt and suspenders and also holdin' your pants up with both hands. Raised the absurdly short timeouts in `customMatchers.ts` (250ms `toPass` → 5000ms, 256ms poll → default). Kept `timeout: 5000` on `.toPass()` calls (defaults to 0), `.waitFor()`, `waitForRequest`, `waitForFunction`, intentionally-short timeouts inside retry loops, and conditional `.isVisible()/.catch()` checks — those fellas actually need the help. ## Review Focus Every remaining timeout in the diff is there for a *reason*. The ones on `.toPass()` stay because that API defaults to zero — it won't retry at all without one. The ones on `.waitFor()` and `waitForRequest` stay because those are locator actions, not auto-retrying assertions. The intentionally-short ones inside `toPass` retry loops (`interaction.spec.ts`) and the negative assertions (`actionbar.spec.ts` confirming no response arrives) — those are *supposed* to be tight. The short timeouts on regular assertions were actively *encouragin'* flaky failures. That's like settin' your alarm for 4 AM and then gettin' mad you're tired. Just... don't do that, man. Let things take the time they need. 38 files, net -115 lines. Less code, more chill. That's livin'. --------- Co-authored-by: Amp <amp@ampcode.com>
237 lines
7.5 KiB
TypeScript
237 lines
7.5 KiB
TypeScript
import { expect } from '@playwright/test'
|
|
|
|
import { comfyPageFixture as test } from '@e2e/fixtures/ComfyPage'
|
|
|
|
const MOCK_FOLDERS: Record<string, string[]> = {
|
|
checkpoints: [
|
|
'sd_xl_base_1.0.safetensors',
|
|
'dreamshaper_8.safetensors',
|
|
'realisticVision_v51.safetensors'
|
|
],
|
|
loras: ['detail_tweaker_xl.safetensors', 'add_brightness.safetensors'],
|
|
vae: ['sdxl_vae.safetensors']
|
|
}
|
|
|
|
// ==========================================================================
|
|
// 1. Tab open/close
|
|
// ==========================================================================
|
|
|
|
test.describe('Model library sidebar - tab', () => {
|
|
test.beforeEach(async ({ comfyPage }) => {
|
|
await comfyPage.modelLibrary.mockFoldersWithFiles(MOCK_FOLDERS)
|
|
await comfyPage.setup()
|
|
})
|
|
|
|
test.afterEach(async ({ comfyPage }) => {
|
|
await comfyPage.modelLibrary.clearMocks()
|
|
})
|
|
|
|
test('Opens model library tab and shows tree', async ({ comfyPage }) => {
|
|
const tab = comfyPage.menu.modelLibraryTab
|
|
await tab.open()
|
|
|
|
await expect(tab.modelTree).toBeVisible()
|
|
await expect(tab.searchInput).toBeVisible()
|
|
})
|
|
|
|
test('Shows refresh and load all folders buttons', async ({ comfyPage }) => {
|
|
const tab = comfyPage.menu.modelLibraryTab
|
|
await tab.open()
|
|
|
|
await expect(tab.refreshButton).toBeVisible()
|
|
await expect(tab.loadAllFoldersButton).toBeVisible()
|
|
})
|
|
})
|
|
|
|
// ==========================================================================
|
|
// 2. Folder display
|
|
// ==========================================================================
|
|
|
|
test.describe('Model library sidebar - folders', () => {
|
|
// Mocks are set up before setup(), so app.ts's loadModelFolders()
|
|
// call during initialization hits the mock and populates the store.
|
|
test.beforeEach(async ({ comfyPage }) => {
|
|
await comfyPage.modelLibrary.mockFoldersWithFiles(MOCK_FOLDERS)
|
|
await comfyPage.setup()
|
|
})
|
|
|
|
test.afterEach(async ({ comfyPage }) => {
|
|
await comfyPage.modelLibrary.clearMocks()
|
|
})
|
|
|
|
test('Displays model folders after opening tab', async ({ comfyPage }) => {
|
|
const tab = comfyPage.menu.modelLibraryTab
|
|
await tab.open()
|
|
|
|
await expect(tab.getFolderByLabel('checkpoints')).toBeVisible()
|
|
await expect(tab.getFolderByLabel('loras')).toBeVisible()
|
|
await expect(tab.getFolderByLabel('vae')).toBeVisible()
|
|
})
|
|
|
|
test('Expanding a folder loads and shows models', async ({ comfyPage }) => {
|
|
const tab = comfyPage.menu.modelLibraryTab
|
|
await tab.open()
|
|
|
|
// Click the folder to expand it
|
|
await tab.getFolderByLabel('checkpoints').click()
|
|
|
|
// Models should appear as leaf nodes
|
|
await expect(tab.getLeafByLabel('sd_xl_base_1.0')).toBeVisible()
|
|
await expect(tab.getLeafByLabel('dreamshaper_8')).toBeVisible()
|
|
await expect(tab.getLeafByLabel('realisticVision_v51')).toBeVisible()
|
|
})
|
|
|
|
test('Expanding a different folder shows its models', async ({
|
|
comfyPage
|
|
}) => {
|
|
const tab = comfyPage.menu.modelLibraryTab
|
|
await tab.open()
|
|
|
|
await tab.getFolderByLabel('loras').click()
|
|
|
|
await expect(tab.getLeafByLabel('detail_tweaker_xl')).toBeVisible()
|
|
await expect(tab.getLeafByLabel('add_brightness')).toBeVisible()
|
|
})
|
|
})
|
|
|
|
// ==========================================================================
|
|
// 3. Search
|
|
// ==========================================================================
|
|
|
|
test.describe('Model library sidebar - search', () => {
|
|
test.beforeEach(async ({ comfyPage }) => {
|
|
await comfyPage.modelLibrary.mockFoldersWithFiles(MOCK_FOLDERS)
|
|
await comfyPage.setup()
|
|
})
|
|
|
|
test.afterEach(async ({ comfyPage }) => {
|
|
await comfyPage.modelLibrary.clearMocks()
|
|
})
|
|
|
|
test('Search filters models by filename', async ({ comfyPage }) => {
|
|
const tab = comfyPage.menu.modelLibraryTab
|
|
await tab.open()
|
|
|
|
await tab.searchInput.fill('dreamshaper')
|
|
|
|
// Wait for debounce (300ms) + load + render
|
|
await expect(tab.getLeafByLabel('dreamshaper_8')).toBeVisible()
|
|
|
|
// Other models should not be visible
|
|
await expect(tab.getLeafByLabel('sd_xl_base_1.0')).not.toBeVisible()
|
|
})
|
|
|
|
test('Clearing search restores folder view', async ({ comfyPage }) => {
|
|
const tab = comfyPage.menu.modelLibraryTab
|
|
await tab.open()
|
|
|
|
await tab.searchInput.fill('dreamshaper')
|
|
await expect(tab.getLeafByLabel('dreamshaper_8')).toBeVisible()
|
|
|
|
// Clear the search
|
|
await tab.searchInput.fill('')
|
|
|
|
// Folders should be visible again (collapsed)
|
|
await expect(tab.getFolderByLabel('checkpoints')).toBeVisible()
|
|
await expect(tab.getFolderByLabel('loras')).toBeVisible()
|
|
})
|
|
|
|
test('Search with no matches shows empty tree', async ({ comfyPage }) => {
|
|
const tab = comfyPage.menu.modelLibraryTab
|
|
await tab.open()
|
|
|
|
// Expand a folder and verify models are present before searching
|
|
await tab.getFolderByLabel('checkpoints').click()
|
|
await expect(tab.leafNodes).not.toHaveCount(0)
|
|
|
|
await tab.searchInput.fill('nonexistent_model_xyz')
|
|
|
|
// Wait for debounce, then verify no leaf nodes
|
|
await expect.poll(() => tab.leafNodes.count()).toBe(0)
|
|
})
|
|
})
|
|
|
|
// ==========================================================================
|
|
// 4. Refresh and load all
|
|
// ==========================================================================
|
|
|
|
test.describe('Model library sidebar - refresh', () => {
|
|
test.afterEach(async ({ comfyPage }) => {
|
|
await comfyPage.modelLibrary.clearMocks()
|
|
})
|
|
|
|
test('Refresh button reloads folder list', async ({ comfyPage }) => {
|
|
await comfyPage.modelLibrary.mockFoldersWithFiles({
|
|
checkpoints: ['model_a.safetensors']
|
|
})
|
|
await comfyPage.setup()
|
|
|
|
const tab = comfyPage.menu.modelLibraryTab
|
|
await tab.open()
|
|
|
|
await expect(tab.getFolderByLabel('checkpoints')).toBeVisible()
|
|
|
|
// Update mock to include a new folder
|
|
await comfyPage.modelLibrary.clearMocks()
|
|
await comfyPage.modelLibrary.mockFoldersWithFiles({
|
|
checkpoints: ['model_a.safetensors'],
|
|
loras: ['lora_b.safetensors']
|
|
})
|
|
|
|
// Wait for the refresh request to complete
|
|
const refreshRequest = comfyPage.page.waitForRequest(
|
|
(req) => req.url().endsWith('/experiment/models'),
|
|
{ timeout: 5000 }
|
|
)
|
|
await tab.refreshButton.click()
|
|
await refreshRequest
|
|
|
|
await expect(tab.getFolderByLabel('loras')).toBeVisible()
|
|
})
|
|
|
|
test('Load all folders button triggers loading all model data', async ({
|
|
comfyPage
|
|
}) => {
|
|
await comfyPage.modelLibrary.mockFoldersWithFiles(MOCK_FOLDERS)
|
|
await comfyPage.setup()
|
|
|
|
const tab = comfyPage.menu.modelLibraryTab
|
|
await tab.open()
|
|
|
|
// Wait for a per-folder model files request triggered by load all
|
|
const folderRequest = comfyPage.page.waitForRequest(
|
|
(req) =>
|
|
/\/api\/experiment\/models\/[^/]+$/.test(req.url()) &&
|
|
req.method() === 'GET',
|
|
{ timeout: 5000 }
|
|
)
|
|
|
|
await tab.loadAllFoldersButton.click()
|
|
await folderRequest
|
|
})
|
|
})
|
|
|
|
// ==========================================================================
|
|
// 5. Empty state
|
|
// ==========================================================================
|
|
|
|
test.describe('Model library sidebar - empty state', () => {
|
|
test.afterEach(async ({ comfyPage }) => {
|
|
await comfyPage.modelLibrary.clearMocks()
|
|
})
|
|
|
|
test('Shows empty tree when no model folders exist', async ({
|
|
comfyPage
|
|
}) => {
|
|
await comfyPage.modelLibrary.mockFoldersWithFiles({})
|
|
await comfyPage.setup()
|
|
|
|
const tab = comfyPage.menu.modelLibraryTab
|
|
await tab.open()
|
|
|
|
await expect(tab.modelTree).toBeVisible()
|
|
await expect(tab.folderNodes).toHaveCount(0)
|
|
await expect(tab.leafNodes).toHaveCount(0)
|
|
})
|
|
})
|