151 lines
4.6 KiB
TypeScript
151 lines
4.6 KiB
TypeScript
/**
|
|
* IQ-Boost/U1: unit tests for AutonomyPausedCard.
|
|
*
|
|
* Mount strategy: native Vue createApp + h (no @vue/test-utils dependency),
|
|
* consistent with BoardBannerCard.test.ts. Verifies:
|
|
* - reason → 中文标签映射 (timeout / consecutive_failures / manual)
|
|
* - progress 摘要渲染 (步骤 N / M · 工具 X)
|
|
* - resume 按钮点击 emit resume 事件携带 resume_token
|
|
*/
|
|
|
|
import { afterEach, describe, expect, it } from 'vitest'
|
|
import { createApp, h, type App } from 'vue'
|
|
import AutonomyPausedCard from '@/components/chat/messages/AutonomyPausedCard.vue'
|
|
import type { IAutonomyPausedData } from '@/api/types'
|
|
|
|
interface Mounted {
|
|
container: HTMLElement
|
|
root: HTMLElement
|
|
app: App
|
|
unmount: () => void
|
|
}
|
|
|
|
function mountCard(
|
|
props: { data: IAutonomyPausedData },
|
|
onResume?: (token: string) => void,
|
|
): Mounted {
|
|
const container = document.createElement('div')
|
|
document.body.appendChild(container)
|
|
const app = createApp({
|
|
setup() {
|
|
return () =>
|
|
h(AutonomyPausedCard as never, {
|
|
data: props.data as never,
|
|
onResume: (token: string) => onResume?.(token),
|
|
} as never)
|
|
},
|
|
})
|
|
app.mount(container)
|
|
const root = container.querySelector('.autonomy-card') as HTMLElement
|
|
return {
|
|
container,
|
|
root,
|
|
app,
|
|
unmount: () => {
|
|
app.unmount()
|
|
container.remove()
|
|
},
|
|
}
|
|
}
|
|
|
|
function makeData(
|
|
overrides: Partial<IAutonomyPausedData> = {},
|
|
): IAutonomyPausedData {
|
|
return {
|
|
reason: 'timeout',
|
|
progress: { step: 3, tool_name: 'shell', total_steps: 10 },
|
|
resume_token: 'autonomy_pause:timeout:3',
|
|
consecutive_failures: 0,
|
|
elapsed_seconds: 305.4,
|
|
...overrides,
|
|
}
|
|
}
|
|
|
|
describe('AutonomyPausedCard (IQ-Boost/U1)', () => {
|
|
let mounted: Mounted | null = null
|
|
|
|
afterEach(() => {
|
|
mounted?.unmount()
|
|
mounted = null
|
|
})
|
|
|
|
it('renders reason=timeout as "自主执行超时"', () => {
|
|
mounted = mountCard({ data: makeData({ reason: 'timeout' }) })
|
|
const reason = mounted.container.querySelector(
|
|
'.autonomy-card__reason',
|
|
) as HTMLElement
|
|
expect(reason).toBeTruthy()
|
|
expect(reason.textContent).toBe('自主执行超时')
|
|
})
|
|
|
|
it('renders reason=consecutive_failures as "连续失败触发暂停"', () => {
|
|
mounted = mountCard({
|
|
data: makeData({ reason: 'consecutive_failures', consecutive_failures: 3 }),
|
|
})
|
|
const reason = mounted.container.querySelector(
|
|
'.autonomy-card__reason',
|
|
) as HTMLElement
|
|
expect(reason.textContent).toBe('连续失败触发暂停')
|
|
// consecutive_failures count is also shown in meta.
|
|
const meta = mounted.container.querySelector(
|
|
'.autonomy-card__meta',
|
|
) as HTMLElement
|
|
expect(meta.textContent).toContain('连续失败 3 次')
|
|
})
|
|
|
|
it('renders reason=manual as "用户手动暂停"', () => {
|
|
mounted = mountCard({ data: makeData({ reason: 'manual' }) })
|
|
const reason = mounted.container.querySelector(
|
|
'.autonomy-card__reason',
|
|
) as HTMLElement
|
|
expect(reason.textContent).toBe('用户手动暂停')
|
|
})
|
|
|
|
it('renders progress summary "步骤 3 / 10 · 工具 shell"', () => {
|
|
mounted = mountCard({ data: makeData() })
|
|
const progress = mounted.container.querySelector(
|
|
'.autonomy-card__progress',
|
|
) as HTMLElement
|
|
expect(progress).toBeTruthy()
|
|
expect(progress.textContent).toContain('步骤 3 / 10')
|
|
expect(progress.textContent).toContain('工具 shell')
|
|
})
|
|
|
|
it('renders elapsed time "已运行 5m 5s" for 305.4s', () => {
|
|
mounted = mountCard({ data: makeData({ elapsed_seconds: 305.4 }) })
|
|
const meta = mounted.container.querySelector(
|
|
'.autonomy-card__meta',
|
|
) as HTMLElement
|
|
expect(meta.textContent).toContain('已运行 5m 5s')
|
|
})
|
|
|
|
it('emits resume with resume_token when 继续 button is clicked', async () => {
|
|
let emittedToken: string | null = null
|
|
mounted = mountCard(
|
|
{ data: makeData({ resume_token: 'autonomy_pause:timeout:7' }) },
|
|
(token) => {
|
|
emittedToken = token
|
|
},
|
|
)
|
|
const button = mounted.container.querySelector(
|
|
'.autonomy-card__actions button',
|
|
) as HTMLButtonElement
|
|
expect(button).toBeTruthy()
|
|
button.click()
|
|
// Vue event flush — await a microtask.
|
|
await Promise.resolve()
|
|
expect(emittedToken).toBe('autonomy_pause:timeout:7')
|
|
})
|
|
|
|
it('hides progress block when progress has no known fields', () => {
|
|
mounted = mountCard({
|
|
data: makeData({ progress: {} }),
|
|
})
|
|
const progress = mounted.container.querySelector(
|
|
'.autonomy-card__progress',
|
|
)
|
|
// v-if="progressSummary" — empty progress → no render.
|
|
expect(progress).toBeNull()
|
|
})
|
|
})
|