mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-07-09 16:48:06 +00:00
## Summary This is the second PR in the planned Workflow Overview error-tab modernization stack. It follows the merged execution-error slice in #12683 and focuses only on Missing Media / Missing Inputs. The intent is to simplify missing-media errors from media-type/file-resolution cards into compact node-level guidance. Missing media is now presented as a flat list of affected node inputs, matching the new catalog-driven item-row direction introduced by the first PR while intentionally avoiding the larger missing-node, swap-node, and missing-model redesigns. This PR also removes the upload/library selection workflow from the error tab. Missing media rows now help the user locate the affected node/input directly, rather than attempting to resolve the missing file inside the error card. ## Changes - **What**: Simplifies the Missing Inputs card into a flat list of affected node/widget rows. - **What**: Displays each missing media row with a catalog-resolved item label in the form `{nodeName} - {inputName}`. - **What**: Makes both the row label and right-aligned locate icon navigate to the affected node on canvas. - **What**: Adds item-specific accessible labels for locate icon buttons and an explicit focus-visible state for clickable row labels. - **What**: Uses the existing `list-scale` transition pattern so missing media rows animate consistently when they enter or leave. - **What**: Removes the old image/video/audio sub-sections from the missing-media card. - **What**: Removes missing-media upload/dropzone/library-selection/status/confirm/cancel UI, because resolution is no longer handled inside this card. - **What**: Removes stale E2E selectors and tests tied to the deleted upload/library controls. - **What**: Removes stale English missing-media locale keys for the deleted controls; translated locale files are intentionally left to the localization sync process. - **What**: Removes stale missing-media store interaction state (`expandState`, `uploadState`, `pendingSelection`) and the now-unused name-based removal API that only supported the deleted upload/library flow. - **What**: Keeps the remaining widget/node/prefix candidate-removal APIs used by the missing-media lifecycle. - **What**: Carries `nodeType` through missing-media referencing nodes so fallback labels remain correct when the same missing filename is referenced by multiple node types. - **Breaking**: None expected. This changes the missing-media error-tab UX, but does not change the workflow schema or backend APIs. - **Dependencies**: None. ## Planned stack This PR is part of the same staged redesign plan described in #12683. The goal is to keep each error category reviewable and stable before moving to the next slice. 1. Execution errors, merged in #12683: validation, runtime, and prompt error grouping/presentation. 2. Missing media, this PR: simplify image/video/audio missing-media cards into node-level catalog item rows with locate actions. 3. Missing node and swap node: align missing-pack rows, nested node references, install/replace actions, and locate behavior. 4. Missing model: unify OSS and Cloud presentation, simplify download/import actions, and improve import/download progress behavior. This PR intentionally does not change missing node packs, swap nodes, missing models, runtime errors, validation errors, or prompt errors. ## Review Focus - Missing media rows should now be node-level rows, not file-level rows and not image/video/audio sections. - If the same missing media filename is referenced by multiple nodes, each affected node/input should render as its own row. - Clicking either the row label or locate icon should focus the corresponding affected node. - Removed upload/library controls are intentional and should not be restored in this PR. - The missing-media store cleanup should not remove any state or API still used by the remaining missing-media lifecycle. - Only `rightSidePanel.missingMedia.missingMediaTitle` should remain in the English locale for the old missing-media UI namespace; new row copy lives in the error catalog. - This PR should stay scoped to Missing Media / Missing Inputs and should not include the larger missing model/node/swap redesign work. ## Screenshots (if applicable) This PR <img width="483" height="339" alt="스크린샷 2026-06-08 오후 8 26 43" src="https://github.com/user-attachments/assets/e029411e-b1db-41cd-aa6e-2bd5d86762f6" /> Before (Main) <img width="484" height="802" alt="스크린샷 2026-06-08 오후 8 32 19" src="https://github.com/user-attachments/assets/c183c18d-babe-4f4e-b4e0-706ce41298de" /> ## Validation - `pnpm format` - `pnpm test:unit src/platform/missingMedia/missingMediaStore.test.ts src/components/rightSidePanel/errors/TabErrors.test.ts src/platform/errorCatalog/errorMessageResolver.test.ts src/platform/missingMedia/missingMediaScan.test.ts src/platform/missingMedia/missingMediaAssetResolver.test.ts` - `pnpm typecheck` - `pnpm knip --cache` - `pnpm test:browser:local browser_tests/tests/propertiesPanel/errorsTabMissingMedia.spec.ts --project=chromium` - Pre-push hook: `pnpm knip --cache` ## Review notes Claude review was run after the main implementation and again after cleanup. No blocker or major issues remained. The only final minor finding was stale inline i18n fixture data in `TabErrors.test.ts`, which was removed in the final commit.
140 lines
4.1 KiB
TypeScript
140 lines
4.1 KiB
TypeScript
import { expect } from '@playwright/test'
|
|
|
|
import type { ComfyPage } from '@e2e/fixtures/ComfyPage'
|
|
import { comfyPageFixture as test } from '@e2e/fixtures/ComfyPage'
|
|
import { TestIds } from '@e2e/fixtures/selectors'
|
|
import { loadWorkflowAndOpenErrorsTab } from '@e2e/fixtures/helpers/ErrorsTabHelper'
|
|
|
|
function getMediaRow(comfyPage: ComfyPage) {
|
|
return comfyPage.page.getByTestId(TestIds.dialogs.missingMediaRow)
|
|
}
|
|
|
|
function getErrorOverlay(comfyPage: ComfyPage) {
|
|
return comfyPage.page.getByTestId(TestIds.dialogs.errorOverlay)
|
|
}
|
|
|
|
test.describe('Errors tab - Missing media', { tag: '@ui' }, () => {
|
|
test.beforeEach(async ({ comfyPage }) => {
|
|
await comfyPage.settings.setSetting(
|
|
'Comfy.RightSidePanel.ShowErrorsTab',
|
|
true
|
|
)
|
|
})
|
|
|
|
test.describe('Detection', () => {
|
|
test('Shows missing media group in errors tab', async ({ comfyPage }) => {
|
|
await comfyPage.workflow.loadWorkflow('missing/missing_media_single')
|
|
|
|
const overlay = getErrorOverlay(comfyPage)
|
|
await expect(overlay).toBeVisible()
|
|
await expect(
|
|
overlay.getByTestId(TestIds.dialogs.errorOverlayMessages)
|
|
).toContainText(/Load Image/)
|
|
|
|
await overlay.getByTestId(TestIds.dialogs.errorOverlaySeeErrors).click()
|
|
await expect(overlay).toBeHidden()
|
|
|
|
const missingMediaGroup = comfyPage.page.getByTestId(
|
|
TestIds.dialogs.missingMediaGroup
|
|
)
|
|
await expect(missingMediaGroup).toBeVisible()
|
|
await expect(
|
|
missingMediaGroup.getByTestId(TestIds.dialogs.errorGroupDisplayMessage)
|
|
).toHaveText(/\S/)
|
|
})
|
|
|
|
test('Shows correct number of missing media rows', async ({
|
|
comfyPage
|
|
}) => {
|
|
await loadWorkflowAndOpenErrorsTab(
|
|
comfyPage,
|
|
'missing/missing_media_multiple'
|
|
)
|
|
|
|
await expect(getMediaRow(comfyPage)).toHaveCount(2)
|
|
})
|
|
|
|
test('Shows missing item label and locate action', async ({
|
|
comfyPage
|
|
}) => {
|
|
await loadWorkflowAndOpenErrorsTab(
|
|
comfyPage,
|
|
'missing/missing_media_single'
|
|
)
|
|
|
|
await expect(getMediaRow(comfyPage)).toHaveText(/Load Image - image/)
|
|
await expect(
|
|
comfyPage.page.getByTestId(TestIds.dialogs.missingMediaLocateButton)
|
|
).toBeVisible()
|
|
})
|
|
})
|
|
|
|
test.describe('List behavior', () => {
|
|
test('Clicking the missing item label navigates canvas to the node', async ({
|
|
comfyPage
|
|
}) => {
|
|
await loadWorkflowAndOpenErrorsTab(
|
|
comfyPage,
|
|
'missing/missing_media_single'
|
|
)
|
|
|
|
const offsetBefore = await comfyPage.page.evaluate(() => {
|
|
const canvas = window['app']?.canvas
|
|
return canvas?.ds?.offset
|
|
? [canvas.ds.offset[0], canvas.ds.offset[1]]
|
|
: null
|
|
})
|
|
|
|
await getMediaRow(comfyPage)
|
|
.getByRole('button', { name: 'Load Image - image', exact: true })
|
|
.click()
|
|
|
|
await expect
|
|
.poll(async () => {
|
|
return await comfyPage.page.evaluate(() => {
|
|
const canvas = window['app']?.canvas
|
|
return canvas?.ds?.offset
|
|
? [canvas.ds.offset[0], canvas.ds.offset[1]]
|
|
: null
|
|
})
|
|
})
|
|
.not.toEqual(offsetBefore)
|
|
})
|
|
})
|
|
|
|
test.describe('Locate node', () => {
|
|
test('Locate button navigates canvas to the missing media node', async ({
|
|
comfyPage
|
|
}) => {
|
|
await loadWorkflowAndOpenErrorsTab(
|
|
comfyPage,
|
|
'missing/missing_media_single'
|
|
)
|
|
|
|
const offsetBefore = await comfyPage.page.evaluate(() => {
|
|
const canvas = window['app']?.canvas
|
|
return canvas?.ds?.offset
|
|
? [canvas.ds.offset[0], canvas.ds.offset[1]]
|
|
: null
|
|
})
|
|
|
|
const locateButton = comfyPage.page.getByTestId(
|
|
TestIds.dialogs.missingMediaLocateButton
|
|
)
|
|
await expect(locateButton).toBeVisible()
|
|
await locateButton.click()
|
|
|
|
await expect
|
|
.poll(async () => {
|
|
return await comfyPage.page.evaluate(() => {
|
|
const canvas = window['app']?.canvas
|
|
return canvas?.ds?.offset
|
|
? [canvas.ds.offset[0], canvas.ds.offset[1]]
|
|
: null
|
|
})
|
|
})
|
|
.not.toEqual(offsetBefore)
|
|
})
|
|
})
|
|
})
|