Files
ComfyUI_frontend/src/composables/useWorkflowTemplateSelectorDialog.test.ts
pythongosssss 31276ff2a6 feat: Show empty workflow dialog when entering app builder with no nodes (#9379)
## Summary

Prompts users to load a template or return to graph when entering
builder mode on an empty workflow

## Screenshots (if applicable)

<img width="627" height="275" alt="image"
src="https://github.com/user-attachments/assets/c1a35dc3-4e8f-4abd-95b9-2f92524e8ebf"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9379-feat-Show-empty-workflow-dialog-when-entering-app-builder-with-no-nodes-3196d73d36508123b643ec893cd86cac)
by [Unito](https://www.unito.io)
2026-03-04 12:15:56 -08:00

159 lines
4.2 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from 'vitest'
const mockDialogService = vi.hoisted(() => ({
showLayoutDialog: vi.fn()
}))
const mockDialogStore = vi.hoisted(() => ({
closeDialog: vi.fn()
}))
const mockNewUserService = vi.hoisted(() => ({
isNewUser: vi.fn()
}))
const mockTelemetry = vi.hoisted(() => ({
trackTemplateLibraryOpened: vi.fn()
}))
vi.mock('@/services/dialogService', () => ({
useDialogService: () => mockDialogService
}))
vi.mock('@/stores/dialogStore', () => ({
useDialogStore: () => mockDialogStore
}))
vi.mock('@/services/useNewUserService', () => ({
useNewUserService: () => mockNewUserService
}))
vi.mock('@/platform/telemetry', () => ({
useTelemetry: () => mockTelemetry
}))
vi.mock(
'@/components/custom/widget/WorkflowTemplateSelectorDialog.vue',
() => ({
default: { name: 'MockWorkflowTemplateSelectorDialog' }
})
)
import { useWorkflowTemplateSelectorDialog } from './useWorkflowTemplateSelectorDialog'
describe('useWorkflowTemplateSelectorDialog', () => {
beforeEach(() => {
vi.clearAllMocks()
})
describe('show', () => {
it('defaults to "all" category for non-new users', () => {
mockNewUserService.isNewUser.mockReturnValue(false)
const dialog = useWorkflowTemplateSelectorDialog()
dialog.show()
expect(mockDialogService.showLayoutDialog).toHaveBeenCalledWith(
expect.objectContaining({
props: expect.objectContaining({
initialCategory: 'all'
})
})
)
})
it('defaults to "basics-getting-started" category for new users', () => {
mockNewUserService.isNewUser.mockReturnValue(true)
const dialog = useWorkflowTemplateSelectorDialog()
dialog.show()
expect(mockDialogService.showLayoutDialog).toHaveBeenCalledWith(
expect.objectContaining({
props: expect.objectContaining({
initialCategory: 'basics-getting-started'
})
})
)
})
it('defaults to "all" when new user status is undetermined', () => {
mockNewUserService.isNewUser.mockReturnValue(null)
const dialog = useWorkflowTemplateSelectorDialog()
dialog.show()
expect(mockDialogService.showLayoutDialog).toHaveBeenCalledWith(
expect.objectContaining({
props: expect.objectContaining({
initialCategory: 'all'
})
})
)
})
it('uses explicit initialCategory when provided', () => {
mockNewUserService.isNewUser.mockReturnValue(true)
const dialog = useWorkflowTemplateSelectorDialog()
dialog.show('command', { initialCategory: 'custom-category' })
expect(mockDialogService.showLayoutDialog).toHaveBeenCalledWith(
expect.objectContaining({
props: expect.objectContaining({
initialCategory: 'custom-category'
})
})
)
})
it('invokes afterClose callback when dialog is closed', () => {
mockNewUserService.isNewUser.mockReturnValue(false)
const afterClose = vi.fn()
const dialog = useWorkflowTemplateSelectorDialog()
dialog.show('command', { afterClose })
const onClose =
mockDialogService.showLayoutDialog.mock.calls[0][0].props.onClose
onClose()
expect(mockDialogStore.closeDialog).toHaveBeenCalled()
expect(afterClose).toHaveBeenCalled()
})
it('does not fail when afterClose is not provided', () => {
mockNewUserService.isNewUser.mockReturnValue(false)
const dialog = useWorkflowTemplateSelectorDialog()
dialog.show('command')
const onClose =
mockDialogService.showLayoutDialog.mock.calls[0][0].props.onClose
expect(() => onClose()).not.toThrow()
})
it('tracks telemetry with source', () => {
mockNewUserService.isNewUser.mockReturnValue(false)
const dialog = useWorkflowTemplateSelectorDialog()
dialog.show('sidebar')
expect(mockTelemetry.trackTemplateLibraryOpened).toHaveBeenCalledWith({
source: 'sidebar'
})
})
})
describe('hide', () => {
it('closes the dialog', () => {
const dialog = useWorkflowTemplateSelectorDialog()
dialog.hide()
expect(mockDialogStore.closeDialog).toHaveBeenCalledWith({
key: 'global-workflow-template-selector'
})
})
})
})