mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-05-23 22:25:05 +00:00
## Summary Comprehensive Playwright E2E tests for the error systems: ErrorDialog, ErrorOverlay, and the errors tab (missing nodes, models, media, execution errors). ## Changes - **What**: - **ErrorDialog** (`errorDialog.spec.ts`, 7 tests): configure/prompt error triggers, Show Report, Copy to Clipboard, Find Issues on GitHub, Contact Support - **ErrorOverlay** (`errorOverlay.spec.ts`, 12 tests): error count labels, per-type button labels (missing nodes/models/media/multiple), See Errors flow (open panel, dismiss, close), undo/redo persistence - **Errors tab — common** (`errorsTab.spec.ts`, 3 tests): tab visibility, search/filter execution errors - **Errors tab — Missing nodes** (`errorsTabMissingNodes.spec.ts`, 5 tests): MissingNodeCard, packs group, expand/collapse, locate button - **Errors tab — Missing models** (`errorsTabMissingModels.spec.ts`, 6 tests): group display, model name, expand/referencing nodes, clipboard copy, OSS Copy URL/Download buttons - **Errors tab — Missing media** (`errorsTabMissingMedia.spec.ts`, 7 tests): migrated from `missingMedia.spec.ts` with detection, upload/library/cancel flows, locate - **Errors tab — Execution** (`errorsTabExecution.spec.ts`, 2 tests): Find on GitHub/Copy buttons, runtime error panel - **Shared helpers**: `ErrorsTabHelper.ts` (openErrorsTabViaSeeErrors), `clipboardSpy.ts` (interceptClipboardWrite/getClipboardText) - **Component changes**: added `data-testid` to `ErrorDialogContent.vue`, `FindIssueButton.vue`, `MissingModelRow.vue`, `MissingModelCard.vue` - **Selectors**: registered all new test IDs in `selectors.ts` - **Test assets**: `missing_nodes_and_media.json` (compound errors), `missing_models_with_nodes.json` (expand/locate) - **Migrations**: error tests from `dialog.spec.ts` → dedicated files, `errorOverlaySeeErrors.spec.ts` → `errorOverlay.spec.ts`, `missingMedia.spec.ts` → `errorsTabMissingMedia.spec.ts` ## Review Focus - OSS tests (`@oss` tag) verify Download/Copy URL buttons appear for models with embedded URLs. - The `missing_models.json` fixture must remain without nodes — adding `CheckpointLoaderSimple` nodes causes directory mismatch in `enrichWithEmbeddedMetadata` that prevents URL enrichment. A separate `missing_models_with_nodes.json` fixture is used for expand/locate tests. ## Cloud tests deferred Missing model cloud environment tests (`@cloud` tag — hidden buttons, import-unsupported notice) are deferred to a follow-up PR. The `comfyPage` fixture cannot bypass the Firebase auth guard in cloud builds, causing `window.app` initialization timeout. A separate infra PR is needed to add cloud auth bypass to the fixture. ## Bug Discovery During testing, a bug was found where the **Locate button for missing nodes in subgraphs fails on initial workflow load**. `collectMissingNodes` in `loadGraphData` captures execution IDs using pre-`configure()` JSON node IDs, but `configure()` triggers subgraph node ID deduplication (PR #8762, always-on since PR #9510) which remaps colliding IDs. This will be addressed in a follow-up PR. - Fixes #10847 (tracked, fix pending in separate PR) ## Testing - 42 new/migrated E2E tests across 8 spec files - All OSS tests pass locally and in CI ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-10848-test-comprehensive-E2E-tests-for-error-dialog-overlay-and-errors-tab-3386d73d36508137a5e4cec8b12fa2fa) by [Unito](https://www.unito.io) --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
164 lines
4.6 KiB
Vue
164 lines
4.6 KiB
Vue
<template>
|
|
<div
|
|
data-testid="error-dialog"
|
|
class="comfy-error-report flex flex-col gap-4"
|
|
>
|
|
<NoResultsPlaceholder
|
|
class="pb-0"
|
|
icon="pi pi-exclamation-circle"
|
|
:title="title"
|
|
:message="error.exceptionMessage"
|
|
text-class="break-words max-w-[60vw]"
|
|
/>
|
|
<template v-if="error.extensionFile">
|
|
<span>{{ t('errorDialog.extensionFileHint') }}:</span>
|
|
<br />
|
|
<span class="font-bold">{{ error.extensionFile }}</span>
|
|
</template>
|
|
|
|
<div class="flex justify-center gap-2">
|
|
<Button
|
|
v-show="!reportOpen"
|
|
data-testid="error-dialog-show-report"
|
|
variant="textonly"
|
|
@click="showReport"
|
|
>
|
|
{{ $t('g.showReport') }}
|
|
</Button>
|
|
<Button
|
|
v-show="!reportOpen"
|
|
data-testid="error-dialog-contact-support"
|
|
variant="textonly"
|
|
@click="showContactSupport"
|
|
>
|
|
{{ $t('issueReport.helpFix') }}
|
|
</Button>
|
|
</div>
|
|
<template v-if="reportOpen">
|
|
<Divider />
|
|
<ScrollPanel class="h-[400px] w-full max-w-[80vw]">
|
|
<pre class="wrap-break-word whitespace-pre-wrap">{{
|
|
reportContent
|
|
}}</pre>
|
|
</ScrollPanel>
|
|
<Divider />
|
|
</template>
|
|
<div class="flex justify-end gap-4">
|
|
<FindIssueButton
|
|
:error-message="error.exceptionMessage"
|
|
:repo-owner="repoOwner"
|
|
:repo-name="repoName"
|
|
/>
|
|
<Button
|
|
v-if="reportOpen"
|
|
data-testid="error-dialog-copy-report"
|
|
@click="copyReportToClipboard"
|
|
>
|
|
<i class="pi pi-copy" />
|
|
{{ $t('g.copyToClipboard') }}
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import Divider from 'primevue/divider'
|
|
import ScrollPanel from 'primevue/scrollpanel'
|
|
import { useToast } from 'primevue/usetoast'
|
|
import { computed, onMounted, ref } from 'vue'
|
|
import { useI18n } from 'vue-i18n'
|
|
|
|
import NoResultsPlaceholder from '@/components/common/NoResultsPlaceholder.vue'
|
|
import FindIssueButton from '@/components/dialog/content/error/FindIssueButton.vue'
|
|
import Button from '@/components/ui/button/Button.vue'
|
|
import { useCopyToClipboard } from '@/composables/useCopyToClipboard'
|
|
import { useTelemetry } from '@/platform/telemetry'
|
|
import { api } from '@/scripts/api'
|
|
import { app } from '@/scripts/app'
|
|
import { useCommandStore } from '@/stores/commandStore'
|
|
import { useSystemStatsStore } from '@/stores/systemStatsStore'
|
|
import { generateErrorReport } from '@/utils/errorReportUtil'
|
|
import type { ErrorReportData } from '@/utils/errorReportUtil'
|
|
|
|
const { error } = defineProps<{
|
|
error: Omit<ErrorReportData, 'workflow' | 'systemStats' | 'serverLogs'> & {
|
|
/**
|
|
* The type of error report to submit.
|
|
* @default 'unknownError'
|
|
*/
|
|
reportType?: string
|
|
/**
|
|
* The file name of the extension that caused the error.
|
|
*/
|
|
extensionFile?: string
|
|
}
|
|
}>()
|
|
|
|
const repoOwner = 'comfyanonymous'
|
|
const repoName = 'ComfyUI'
|
|
const reportContent = ref('')
|
|
const reportOpen = ref(false)
|
|
/**
|
|
* Open the error report content and track telemetry.
|
|
*/
|
|
const showReport = () => {
|
|
useTelemetry()?.trackUiButtonClicked({
|
|
button_id: 'error_dialog_show_report_clicked'
|
|
})
|
|
reportOpen.value = true
|
|
}
|
|
const toast = useToast()
|
|
const { t } = useI18n()
|
|
const systemStatsStore = useSystemStatsStore()
|
|
const telemetry = useTelemetry()
|
|
|
|
const title = computed<string>(
|
|
() => error.nodeType ?? error.exceptionType ?? t('errorDialog.defaultTitle')
|
|
)
|
|
|
|
/**
|
|
* Open contact support flow from error dialog and track telemetry.
|
|
*/
|
|
const showContactSupport = async () => {
|
|
telemetry?.trackHelpResourceClicked({
|
|
resource_type: 'help_feedback',
|
|
is_external: true,
|
|
source: 'error_dialog'
|
|
})
|
|
await useCommandStore().execute('Comfy.ContactSupport')
|
|
}
|
|
|
|
onMounted(async () => {
|
|
if (!systemStatsStore.systemStats) {
|
|
await systemStatsStore.refetchSystemStats()
|
|
}
|
|
|
|
try {
|
|
const [logs] = await Promise.all([api.getLogs()])
|
|
|
|
reportContent.value = generateErrorReport({
|
|
systemStats: systemStatsStore.systemStats!,
|
|
serverLogs: logs,
|
|
workflow: app.rootGraph.serialize(),
|
|
exceptionType: error.exceptionType,
|
|
exceptionMessage: error.exceptionMessage,
|
|
traceback: error.traceback,
|
|
nodeId: error.nodeId,
|
|
nodeType: error.nodeType
|
|
})
|
|
} catch (error) {
|
|
console.error('Error fetching logs:', error)
|
|
toast.add({
|
|
severity: 'error',
|
|
summary: t('g.error'),
|
|
detail: t('toastMessages.failedToFetchLogs')
|
|
})
|
|
}
|
|
})
|
|
|
|
const { copyToClipboard } = useCopyToClipboard()
|
|
const copyReportToClipboard = async () => {
|
|
await copyToClipboard(reportContent.value)
|
|
}
|
|
</script>
|