mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-20 14:30:41 +00:00
## Summary Makes it easier and more obvious for users to navigate between steps ## Changes - **What**: - add back/next navigation to builder footer alongside exit button - extract shared step logic into useBuilderSteps composable ## Screenshots (if applicable) <img width="428" height="102" alt="image" src="https://github.com/user-attachments/assets/91b33e8f-53ae-4895-a2eb-fb1316b2b367" /> ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-9378-feat-Replace-BuilderExitButton-with-new-BuilderFooterToolbar-3196d73d3650819392efc171cf277326) by [Unito](https://www.unito.io)
148 lines
4.0 KiB
TypeScript
148 lines
4.0 KiB
TypeScript
import { mount } from '@vue/test-utils'
|
|
import { createPinia, setActivePinia } from 'pinia'
|
|
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
import { computed, ref } from 'vue'
|
|
import { createI18n } from 'vue-i18n'
|
|
|
|
import type { AppMode } from '@/composables/useAppMode'
|
|
|
|
import BuilderFooterToolbar from '@/components/builder/BuilderFooterToolbar.vue'
|
|
|
|
const mockSetMode = vi.hoisted(() => vi.fn())
|
|
const mockExitBuilder = vi.hoisted(() => vi.fn())
|
|
const mockShowDialog = vi.hoisted(() => vi.fn())
|
|
|
|
const mockState = {
|
|
mode: 'builder:select' as AppMode,
|
|
settingView: false
|
|
}
|
|
|
|
vi.mock('@/composables/useAppMode', () => ({
|
|
useAppMode: () => ({
|
|
mode: computed(() => mockState.mode),
|
|
isBuilderMode: ref(true),
|
|
setMode: mockSetMode
|
|
})
|
|
}))
|
|
|
|
const mockHasOutputs = ref(true)
|
|
|
|
vi.mock('@/stores/appModeStore', () => ({
|
|
useAppModeStore: () => ({
|
|
exitBuilder: mockExitBuilder,
|
|
hasOutputs: mockHasOutputs,
|
|
$id: 'appMode'
|
|
})
|
|
}))
|
|
|
|
vi.mock('@/stores/dialogStore', () => ({
|
|
useDialogStore: () => ({
|
|
dialogStack: []
|
|
})
|
|
}))
|
|
|
|
vi.mock('@/components/builder/useAppSetDefaultView', () => ({
|
|
useAppSetDefaultView: () => ({
|
|
settingView: computed(() => mockState.settingView),
|
|
showDialog: mockShowDialog
|
|
})
|
|
}))
|
|
|
|
const i18n = createI18n({
|
|
legacy: false,
|
|
locale: 'en',
|
|
messages: {
|
|
en: {
|
|
builderMenu: { exitAppBuilder: 'Exit app builder' },
|
|
g: { back: 'Back', next: 'Next' }
|
|
}
|
|
}
|
|
})
|
|
|
|
describe('BuilderFooterToolbar', () => {
|
|
beforeEach(() => {
|
|
setActivePinia(createPinia())
|
|
vi.clearAllMocks()
|
|
mockState.mode = 'builder:select'
|
|
mockHasOutputs.value = true
|
|
mockState.settingView = false
|
|
})
|
|
|
|
function mountComponent() {
|
|
return mount(BuilderFooterToolbar, {
|
|
global: {
|
|
plugins: [i18n],
|
|
stubs: { Button: false }
|
|
}
|
|
})
|
|
}
|
|
|
|
function getButtons(wrapper: ReturnType<typeof mountComponent>) {
|
|
const buttons = wrapper.findAll('button')
|
|
return {
|
|
exit: buttons[0],
|
|
back: buttons[1],
|
|
next: buttons[2]
|
|
}
|
|
}
|
|
|
|
it('disables back on the first step', () => {
|
|
mockState.mode = 'builder:select'
|
|
const { back } = getButtons(mountComponent())
|
|
expect(back.attributes('disabled')).toBeDefined()
|
|
})
|
|
|
|
it('enables back on the second step', () => {
|
|
mockState.mode = 'builder:arrange'
|
|
const { back } = getButtons(mountComponent())
|
|
expect(back.attributes('disabled')).toBeUndefined()
|
|
})
|
|
|
|
it('disables next on the setDefaultView step', () => {
|
|
mockState.settingView = true
|
|
const { next } = getButtons(mountComponent())
|
|
expect(next.attributes('disabled')).toBeDefined()
|
|
})
|
|
|
|
it('disables next on arrange step when no outputs', () => {
|
|
mockState.mode = 'builder:arrange'
|
|
mockHasOutputs.value = false
|
|
const { next } = getButtons(mountComponent())
|
|
expect(next.attributes('disabled')).toBeDefined()
|
|
})
|
|
|
|
it('enables next on select step', () => {
|
|
mockState.mode = 'builder:select'
|
|
const { next } = getButtons(mountComponent())
|
|
expect(next.attributes('disabled')).toBeUndefined()
|
|
})
|
|
|
|
it('calls setMode on back click', async () => {
|
|
mockState.mode = 'builder:arrange'
|
|
const { back } = getButtons(mountComponent())
|
|
await back.trigger('click')
|
|
expect(mockSetMode).toHaveBeenCalledWith('builder:select')
|
|
})
|
|
|
|
it('calls setMode on next click from select step', async () => {
|
|
mockState.mode = 'builder:select'
|
|
const { next } = getButtons(mountComponent())
|
|
await next.trigger('click')
|
|
expect(mockSetMode).toHaveBeenCalledWith('builder:arrange')
|
|
})
|
|
|
|
it('opens default view dialog on next click from arrange step', async () => {
|
|
mockState.mode = 'builder:arrange'
|
|
const { next } = getButtons(mountComponent())
|
|
await next.trigger('click')
|
|
expect(mockSetMode).toHaveBeenCalledWith('builder:arrange')
|
|
expect(mockShowDialog).toHaveBeenCalledOnce()
|
|
})
|
|
|
|
it('calls exitBuilder on exit button click', async () => {
|
|
const { exit } = getButtons(mountComponent())
|
|
await exit.trigger('click')
|
|
expect(mockExitBuilder).toHaveBeenCalledOnce()
|
|
})
|
|
})
|