mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-07-10 00:58:01 +00:00
## Summary Adds an app mode validation warning so users can see when a workflow has errors before running and jump directly back to graph mode to review them. ## Changes - **What**: Adds a reusable app mode warning banner above the Run button when the execution error store reports workflow errors, including validation and missing asset states. - **What**: Reuses the existing graph-error navigation flow so the warning action switches out of app mode and opens the Errors panel in graph mode. - **What**: Updates the app mode Run button icon and accessible label in the warning state while keeping the Run action non-blocking. - **What**: Adds unit coverage for the warning render/accessibility state and an E2E flow that triggers a validation failure, dismisses the overlay, and opens graph errors from the app mode warning. - **Breaking**: None. - **Dependencies**: None. ## Review Focus The warning intentionally mirrors graph mode behavior: it surfaces the error state but does not prevent the user from clicking Run. This avoids turning display-level validation signals into hard execution blockers. The warning is driven by the existing `hasAnyError` aggregate, so missing nodes, missing models, and missing media are included alongside prompt/node/execution errors. ## Tests - `pnpm format` - `pnpm lint` - `pnpm typecheck` - `pnpm test:unit` - `pnpm knip` - `pnpm test:browser:local browser_tests/tests/appModeValidationWarning.spec.ts` ## Screenshots <img width="461" height="994" alt="스크린샷 2026-06-25 오후 7 00 55" src="https://github.com/user-attachments/assets/f8fc20bf-d572-46b5-9fa4-312e7c4c8076" />
107 lines
3.2 KiB
TypeScript
107 lines
3.2 KiB
TypeScript
import {
|
|
comfyExpect as expect,
|
|
comfyPageFixture as test
|
|
} from '@e2e/fixtures/ComfyPage'
|
|
import type { NodeError, PromptResponse } from '@/schemas/apiSchema'
|
|
import { ExecutionHelper } from '@e2e/fixtures/helpers/ExecutionHelper'
|
|
import { enableErrorsOverlay } from '@e2e/fixtures/helpers/ErrorsTabHelper'
|
|
import { TestIds } from '@e2e/fixtures/selectors'
|
|
|
|
const SAVE_IMAGE_NODE_ID = '9'
|
|
|
|
function buildSaveImageRequiredInputError(): NodeError {
|
|
return {
|
|
class_type: 'SaveImage',
|
|
dependent_outputs: [],
|
|
errors: [
|
|
{
|
|
type: 'required_input_missing',
|
|
message: 'Required input is missing: images',
|
|
details: '',
|
|
extra_info: { input_name: 'images' }
|
|
}
|
|
]
|
|
}
|
|
}
|
|
|
|
test.describe(
|
|
'App mode validation warning',
|
|
{ tag: ['@ui', '@workflow'] },
|
|
() => {
|
|
test.beforeEach(async ({ comfyPage }) => {
|
|
await enableErrorsOverlay(comfyPage)
|
|
await comfyPage.workflow.loadWorkflow('linear-validation-warning')
|
|
await comfyPage.appMode.toggleAppMode()
|
|
await expect(comfyPage.appMode.linearWidgets).toBeVisible()
|
|
})
|
|
|
|
test('opens graph errors from the app mode validation warning', async ({
|
|
comfyPage
|
|
}) => {
|
|
await expect(comfyPage.appMode.validationWarning).toBeHidden()
|
|
|
|
const exec = new ExecutionHelper(comfyPage)
|
|
await exec.mockValidationFailure({
|
|
[SAVE_IMAGE_NODE_ID]: buildSaveImageRequiredInputError()
|
|
})
|
|
|
|
await comfyPage.appMode.runButton.click()
|
|
const appModeOverlay = comfyPage.appMode.centerPanel.getByTestId(
|
|
TestIds.dialogs.errorOverlay
|
|
)
|
|
await expect(appModeOverlay).toBeHidden()
|
|
|
|
await expect(comfyPage.appMode.validationWarning).toBeVisible()
|
|
await expect(comfyPage.appMode.validationWarning).toContainText(
|
|
/Required input missing/i
|
|
)
|
|
await expect(comfyPage.appMode.viewErrorsInGraphButton).toBeVisible()
|
|
|
|
await comfyPage.appMode.viewErrorsInGraphButton.click()
|
|
|
|
await expect(comfyPage.appMode.linearWidgets).toBeHidden()
|
|
await expect(
|
|
comfyPage.page.getByTestId(TestIds.propertiesPanel.root)
|
|
).toBeVisible()
|
|
await expect(
|
|
comfyPage.page.getByTestId(TestIds.propertiesPanel.errorsTab)
|
|
).toBeVisible()
|
|
})
|
|
|
|
test('keeps the app mode run button enabled when the warning is visible', async ({
|
|
comfyPage
|
|
}) => {
|
|
const exec = new ExecutionHelper(comfyPage)
|
|
await exec.mockValidationFailure({
|
|
[SAVE_IMAGE_NODE_ID]: buildSaveImageRequiredInputError()
|
|
})
|
|
|
|
await comfyPage.appMode.runButton.click()
|
|
await expect(comfyPage.appMode.validationWarning).toBeVisible()
|
|
await expect(comfyPage.appMode.runButton).toBeEnabled()
|
|
|
|
let promptQueued = false
|
|
const mockResponse: PromptResponse = {
|
|
prompt_id: 'test-id',
|
|
node_errors: {},
|
|
error: ''
|
|
}
|
|
await comfyPage.page.route(
|
|
'**/api/prompt',
|
|
async (route) => {
|
|
promptQueued = true
|
|
await route.fulfill({
|
|
status: 200,
|
|
body: JSON.stringify(mockResponse)
|
|
})
|
|
},
|
|
{ times: 1 }
|
|
)
|
|
|
|
await comfyPage.appMode.runButton.click()
|
|
|
|
await expect.poll(() => promptQueued).toBe(true)
|
|
})
|
|
}
|
|
)
|