test(studio): Task.vue 单测(3)——补齐 U6 创作流组件覆盖

src/views/create/Task.spec.ts:验进入即轮询(getTaskChain)、终态完成(2)+versionId 自动跳预览(带 gameId)、终态失败(3)不跳预览且重生成→regenerate→跳新任务链。真 store+mock studioApi 保 currentChain 响应式(watch 触发)。

至此 U6 创作流 4 组件(Create/Task/Modify/store)全有 spec;`npm test` 19/19 绿;build 绿。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
lili 2026-06-17 16:42:49 -07:00
parent 7980c717be
commit 439115d8b5

View File

@ -0,0 +1,87 @@
/**
* Task.spec.ts plan 002 U6
* :进入即轮询;(2)(versionId+gameId);(3)
* store + mock studioApi( currentChain ,watch )+ mock router `npm test`
*/
import { describe, it, expect, vi, beforeEach } from 'vitest'
import { mount, flushPromises } from '@vue/test-utils'
import { setActivePinia, createPinia } from 'pinia'
import { createI18n } from 'vue-i18n'
const replace = vi.fn()
const push = vi.fn()
vi.mock('vue-router', () => ({ useRouter: () => ({ replace, push }) }))
vi.mock('../../api/studio', () => ({
getTemplates: vi.fn(),
createGame: vi.fn(),
modifyGame: vi.fn(),
extendGame: vi.fn(),
getTaskChain: vi.fn(),
regenerate: vi.fn(),
}))
import * as studioApi from '../../api/studio'
import Task from './Task.vue'
const api = studioApi as unknown as Record<string, ReturnType<typeof vi.fn>>
const i18n = createI18n({ legacy: false, locale: 'zh-CN', messages: { 'zh-CN': {} }, missingWarn: false, fallbackWarn: false })
const AppButtonStub = {
props: ['disabled', 'loading', 'block', 'size', 'type'],
emits: ['click'],
template: '<button class="app-btn" :disabled="disabled" @click="$emit(\'click\')"><slot/></button>',
}
function mountTask(taskId = '1001') {
setActivePinia(createPinia())
return mount(Task, {
props: { taskId },
global: {
plugins: [i18n],
stubs: { LoadingBar: true, Icon: true, AppButton: AppButtonStub, EmptyState: { template: '<div class="empty"><slot name="action"/></div>' } },
},
})
}
describe('Task.vue生成进度页·studio 任务链)', () => {
beforeEach(() => {
vi.clearAllMocks()
replace.mockClear()
push.mockClear()
})
it('进入即对 taskChainId 启动轮询(getTaskChain)', async () => {
api.getTaskChain.mockResolvedValue({ id: 1001, gameId: 1, status: 1, progress: 20 })
mountTask('1001')
await flushPromises()
expect(api.getTaskChain).toHaveBeenCalledWith(1001)
})
it('终态完成(2)+versionId → 自动 router.replace 跳预览(带 gameId)', async () => {
api.getTaskChain.mockResolvedValue({ id: 1001, gameId: 1, status: 2, progress: 100, versionId: 9001 })
mountTask('1001')
await flushPromises()
expect(replace).toHaveBeenCalledOnce()
const nav = replace.mock.calls[0][0]
expect(nav.name).toBe('create-preview')
expect(nav.params.versionId).toBe('9001')
expect(nav.query.gameId).toBe('1')
})
it('终态失败(3) → 不跳预览;点重生成 → regenerate 并跳新任务链', async () => {
api.getTaskChain.mockResolvedValue({ id: 1001, gameId: 1, status: 3, progress: 0 })
api.regenerate.mockResolvedValue({ id: 1009, gameId: 1, status: 1, progress: 0, retryOf: 1001 })
const w = mountTask('1001')
await flushPromises()
expect(replace).not.toHaveBeenCalled() // 失败不跳预览
// 失败态有重生成按钮(EmptyState #action 内 primary 按钮)
const btns = w.findAll('.app-btn')
expect(btns.length).toBeGreaterThan(0)
await btns[btns.length - 1].trigger('click') // 重生成(末按钮=primary)
await flushPromises()
expect(api.regenerate).toHaveBeenCalledWith(1001)
const nav = replace.mock.calls[0][0]
expect(nav.name).toBe('create-task')
expect(nav.params.taskId).toBe('1009')
})
})