mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-20 06:20:11 +00:00
fix: resolve cloud/1.41 unit test regressions (#10269)
## Summary Fixes three cloud/1.41 test regressions by restoring the intended legacy topbar mutation scheduling behavior, re-enabling multi-output test mocking in assetsStore tests, and backporting execution ID locator caching in executionStore. ## Changes - **What**: Reintroduced RAF-coalesced legacy topbar content checks in `TopMenuSection`, restored `mockOutputOverrides` handling in `assetsStore.test.ts`, and added cached execution-id-to-locator resolution in `executionStore` with cache resets at execution boundaries. ## Review Focus Confirm the cache lifecycle boundaries in `executionStore` (`handleExecutionStart` and `resetExecutionState`) and that the `TopMenuSection` mutation observer now matches the coalescing contract expected by unit tests. Co-authored-by: Amp <amp@ampcode.com>
This commit is contained in:
@@ -117,7 +117,7 @@
|
||||
<script setup lang="ts">
|
||||
import { useLocalStorage, useMutationObserver } from '@vueuse/core'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { computed, onBeforeUnmount, onMounted, ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import ComfyActionbar from '@/components/actionbar/ComfyActionbar.vue'
|
||||
@@ -265,6 +265,7 @@ const rightSidePanelTooltipConfig = computed(() =>
|
||||
// Maintain support for legacy topbar elements attached by custom scripts
|
||||
const legacyCommandsContainerRef = ref<HTMLElement>()
|
||||
const hasLegacyContent = ref(false)
|
||||
let legacyContentCheckRafId: number | null = null
|
||||
|
||||
function checkLegacyContent() {
|
||||
const el = legacyCommandsContainerRef.value
|
||||
@@ -277,7 +278,16 @@ function checkLegacyContent() {
|
||||
el.querySelector(':scope > * > *:not(:empty)') !== null
|
||||
}
|
||||
|
||||
useMutationObserver(legacyCommandsContainerRef, checkLegacyContent, {
|
||||
function scheduleLegacyContentCheck() {
|
||||
if (legacyContentCheckRafId !== null) return
|
||||
|
||||
legacyContentCheckRafId = requestAnimationFrame(() => {
|
||||
legacyContentCheckRafId = null
|
||||
checkLegacyContent()
|
||||
})
|
||||
}
|
||||
|
||||
useMutationObserver(legacyCommandsContainerRef, scheduleLegacyContentCheck, {
|
||||
childList: true,
|
||||
subtree: true,
|
||||
characterData: true
|
||||
@@ -291,6 +301,13 @@ onMounted(() => {
|
||||
}
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (legacyContentCheckRafId === null) return
|
||||
|
||||
cancelAnimationFrame(legacyContentCheckRafId)
|
||||
legacyContentCheckRafId = null
|
||||
})
|
||||
|
||||
const openCustomNodeManager = async () => {
|
||||
try {
|
||||
await managerState.openManager({
|
||||
|
||||
@@ -111,6 +111,18 @@ vi.mock('@/stores/queueStore', () => ({
|
||||
constructor(public job: JobListItem) {
|
||||
this.jobId = job.id
|
||||
this.outputsCount = job.outputs_count ?? null
|
||||
|
||||
if (mockOutputOverrides.value) {
|
||||
this.flatOutputs = mockOutputOverrides.value
|
||||
const previewable = mockOutputOverrides.value.filter(
|
||||
(output) => output.supportsPreview
|
||||
)
|
||||
this.previewOutput =
|
||||
previewable.findLast((output) => output.type === 'output') ??
|
||||
previewable.at(-1)
|
||||
return
|
||||
}
|
||||
|
||||
const preview = job.preview_output
|
||||
const isPreviewable =
|
||||
!!preview?.filename && PREVIEWABLE_MEDIA_TYPES.has(preview.mediaType)
|
||||
|
||||
@@ -80,6 +80,24 @@ export const useExecutionStore = defineStore('execution', () => {
|
||||
|
||||
const initializingJobIds = ref<Set<string>>(new Set())
|
||||
|
||||
/**
|
||||
* Cache executionIdToNodeLocatorId lookups to avoid repeated graph
|
||||
* traversals while progress updates stream for the same execution IDs.
|
||||
*/
|
||||
const executionIdToLocatorCache = new Map<string, NodeLocatorId | undefined>()
|
||||
|
||||
function cachedExecutionIdToLocator(
|
||||
executionId: string
|
||||
): NodeLocatorId | undefined {
|
||||
if (executionIdToLocatorCache.has(executionId)) {
|
||||
return executionIdToLocatorCache.get(executionId)
|
||||
}
|
||||
|
||||
const locatorId = executionIdToNodeLocatorId(app.rootGraph, executionId)
|
||||
executionIdToLocatorCache.set(executionId, locatorId)
|
||||
return locatorId
|
||||
}
|
||||
|
||||
const mergeExecutionProgressStates = (
|
||||
currentState: NodeProgressState | undefined,
|
||||
newState: NodeProgressState
|
||||
@@ -119,7 +137,7 @@ export const useExecutionStore = defineStore('execution', () => {
|
||||
const parts = String(state.display_node_id).split(':')
|
||||
for (let i = 0; i < parts.length; i++) {
|
||||
const executionId = parts.slice(0, i + 1).join(':')
|
||||
const locatorId = executionIdToNodeLocatorId(app.rootGraph, executionId)
|
||||
const locatorId = cachedExecutionIdToLocator(executionId)
|
||||
if (!locatorId) continue
|
||||
|
||||
result[locatorId] = mergeExecutionProgressStates(
|
||||
@@ -227,6 +245,7 @@ export const useExecutionStore = defineStore('execution', () => {
|
||||
}
|
||||
|
||||
function handleExecutionStart(e: CustomEvent<ExecutionStartWsMessage>) {
|
||||
executionIdToLocatorCache.clear()
|
||||
executionErrorStore.clearAllErrors()
|
||||
activeJobId.value = e.detail.prompt_id
|
||||
queuedJobs.value[activeJobId.value] ??= { nodes: {} }
|
||||
@@ -473,6 +492,7 @@ export const useExecutionStore = defineStore('execution', () => {
|
||||
* Reset execution-related state after a run completes or is stopped.
|
||||
*/
|
||||
function resetExecutionState(jobIdParam?: string | null) {
|
||||
executionIdToLocatorCache.clear()
|
||||
nodeProgressStates.value = {}
|
||||
const jobId = jobIdParam ?? activeJobId.value ?? null
|
||||
if (jobId) {
|
||||
|
||||
Reference in New Issue
Block a user