mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-03-14 17:37:46 +00:00
refactor: parallelize bootstrap and simplify lifecycle with VueUse (#8307)
## Summary Refactors bootstrap and lifecycle management to parallelize initialization, use Vue best practices, and fix a logout state bug. ## Changes ### Bootstrap Store (`bootstrapStore.ts`) - Extract early bootstrap logic into a dedicated store using `useAsyncState` - Parallelize settings, i18n, and workflow sync loading (previously sequential) - Handle multi-user login scenarios by deferring settings/workflows until authenticated ### GraphCanvas Refactoring - Move non-DOM composables (`useGlobalLitegraph`, `useCopy`, `usePaste`, etc.) to script setup level for earlier initialization - Move `watch` and `whenever` declarations outside `onMounted` (Vue best practice) - Use `until()` from VueUse to await bootstrap store readiness instead of direct async calls ### GraphView Simplification - Replace manual `addEventListener`/`removeEventListener` with `useEventListener` - Replace `setInterval` with `useIntervalFn` for automatic cleanup - Move core command registration to script setup level ### Bug Fix Using `router.push()` for logout caused `isSettingsReady` to persist as `true`, making new users inherit the previous user's cached settings. Reverted to `window.location.reload()` with TODOs for future store reset implementation. ## Testing - Verified login/logout cycle clears settings correctly - Verified bootstrap sequence completes without errors --------- Co-authored-by: Amp <amp@ampcode.com>
This commit is contained in:
115
src/stores/bootstrapStore.test.ts
Normal file
115
src/stores/bootstrapStore.test.ts
Normal file
@@ -0,0 +1,115 @@
|
||||
import { createTestingPinia } from '@pinia/testing'
|
||||
import { setActivePinia } from 'pinia'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { ref } from 'vue'
|
||||
|
||||
import { useSettingStore } from '@/platform/settings/settingStore'
|
||||
|
||||
import { useBootstrapStore } from './bootstrapStore'
|
||||
|
||||
vi.mock('@/scripts/api', () => ({
|
||||
api: {
|
||||
init: vi.fn().mockResolvedValue(undefined),
|
||||
getNodeDefs: vi.fn().mockResolvedValue({ TestNode: { name: 'TestNode' } }),
|
||||
getCustomNodesI18n: vi.fn().mockResolvedValue({}),
|
||||
getUserConfig: vi.fn().mockResolvedValue({})
|
||||
}
|
||||
}))
|
||||
|
||||
vi.mock('@/i18n', () => ({
|
||||
mergeCustomNodesI18n: vi.fn()
|
||||
}))
|
||||
|
||||
const mockIsSettingsReady = ref(false)
|
||||
|
||||
vi.mock('@/platform/settings/settingStore', () => ({
|
||||
useSettingStore: vi.fn(() => ({
|
||||
load: vi.fn(() => {
|
||||
mockIsSettingsReady.value = true
|
||||
}),
|
||||
get isReady() {
|
||||
return mockIsSettingsReady.value
|
||||
},
|
||||
isLoading: ref(false),
|
||||
error: ref(undefined)
|
||||
}))
|
||||
}))
|
||||
|
||||
vi.mock('@/platform/workflow/management/stores/workflowStore', () => ({
|
||||
useWorkflowStore: vi.fn(() => ({
|
||||
loadWorkflows: vi.fn(),
|
||||
syncWorkflows: vi.fn().mockResolvedValue(undefined)
|
||||
}))
|
||||
}))
|
||||
|
||||
vi.mock('@/stores/userStore', () => ({
|
||||
useUserStore: vi.fn(() => ({
|
||||
initialize: vi.fn().mockResolvedValue(undefined),
|
||||
needsLogin: false
|
||||
}))
|
||||
}))
|
||||
|
||||
const mockIsFirebaseInitialized = ref(false)
|
||||
vi.mock('@/stores/firebaseAuthStore', () => ({
|
||||
useFirebaseAuthStore: vi.fn(() => ({
|
||||
isInitialized: mockIsFirebaseInitialized
|
||||
}))
|
||||
}))
|
||||
|
||||
const mockDistributionTypes = vi.hoisted(() => ({
|
||||
isCloud: false
|
||||
}))
|
||||
vi.mock('@/platform/distribution/types', () => mockDistributionTypes)
|
||||
|
||||
describe('bootstrapStore', () => {
|
||||
beforeEach(() => {
|
||||
mockIsSettingsReady.value = false
|
||||
mockIsFirebaseInitialized.value = false
|
||||
mockDistributionTypes.isCloud = false
|
||||
setActivePinia(createTestingPinia({ stubActions: false }))
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
it('initializes with all flags false', () => {
|
||||
const store = useBootstrapStore()
|
||||
const settingStore = useSettingStore()
|
||||
expect(settingStore.isReady).toBe(false)
|
||||
expect(store.isI18nReady).toBe(false)
|
||||
})
|
||||
|
||||
it('starts store bootstrap (settings, i18n)', async () => {
|
||||
const store = useBootstrapStore()
|
||||
const settingStore = useSettingStore()
|
||||
void store.startStoreBootstrap()
|
||||
|
||||
await vi.waitFor(() => {
|
||||
expect(settingStore.isReady).toBe(true)
|
||||
expect(store.isI18nReady).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('cloud mode', () => {
|
||||
beforeEach(() => {
|
||||
mockDistributionTypes.isCloud = true
|
||||
})
|
||||
|
||||
it('waits for Firebase auth before loading i18n and settings', async () => {
|
||||
const store = useBootstrapStore()
|
||||
const settingStore = useSettingStore()
|
||||
const bootstrapPromise = store.startStoreBootstrap()
|
||||
|
||||
// Bootstrap is blocked waiting for firebase
|
||||
expect(store.isI18nReady).toBe(false)
|
||||
expect(settingStore.isReady).toBe(false)
|
||||
|
||||
// Unblock by initializing firebase
|
||||
mockIsFirebaseInitialized.value = true
|
||||
await bootstrapPromise
|
||||
|
||||
await vi.waitFor(() => {
|
||||
expect(store.isI18nReady).toBe(true)
|
||||
expect(settingStore.isReady).toBe(true)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user