Files
X-Financial/web/tests/ai-expense-draft-model.test.mjs
caoxiaozhu c4b5fcc067 feat(web): AI 工作台多 task 串行推进与会话适配
- useWorkbenchAiApplicationPreviewFlow/useWorkbenchAiActionRouter/useWorkbenchAiCommandIntents 支持 task1 完成后自动推进 task2,确认按钮直接拉起申请预览,草稿/提交成功后继续推进下一 task
- workbenchAiIntentPlannerModel/workbenchAiMessageModel/workbenchAiCommandIntentModel 适配多 task 意图规划与消息结构
- aiApplicationPreviewActions/aiApplicationPrecheckModel/aiExpenseDraftModel/aiWorkbenchConversationStore 草稿与会话存储适配
- PersonalWorkbenchAiMode 与样式适配,更新 preview-actions/expense-draft/conversation-store/fast-preview/action-router/command-intent/intent-planner 测试
2026-06-26 22:42:23 +08:00

113 lines
4.1 KiB
JavaScript

import assert from 'node:assert/strict'
import test from 'node:test'
import {
applyAiExpenseAnswer,
buildAiExpenseDraftPrefillValues,
buildAiExpenseStepPrompt,
buildAiExpenseSummary,
createAiExpenseDraft,
getAiExpenseCurrentStep,
isAiExpenseDraftComplete
} from '../src/utils/aiExpenseDraftModel.js'
test('draft starts at the reason step regardless of expense type', () => {
const draft = createAiExpenseDraft('transport', '交通费')
assert.equal(draft.expenseType, 'transport')
assert.equal(draft.expenseTypeLabel, '交通费')
assert.equal(draft.stepKey, 'reason')
assert.equal(getAiExpenseCurrentStep(draft).key, 'reason')
})
test('answers advance through fields in order and reach the summary step', () => {
let draft = createAiExpenseDraft('office', '办公用品费')
draft = applyAiExpenseAnswer(draft, '项目现场临时采购', [])
assert.equal(draft.stepKey, 'time_range')
draft = applyAiExpenseAnswer(draft, '2026-06-15', [])
assert.equal(draft.stepKey, 'location')
draft = applyAiExpenseAnswer(draft, '京东', [])
assert.equal(draft.stepKey, 'amount')
draft = applyAiExpenseAnswer(draft, '320元', [])
assert.equal(draft.stepKey, 'attachments')
draft = applyAiExpenseAnswer(draft, '稍后上传', [])
assert.ok(isAiExpenseDraftComplete(draft))
})
test('attachments step collects uploaded file names', () => {
let draft = createAiExpenseDraft('office', '办公用品费')
draft = applyAiExpenseAnswer(draft, '事由', [])
draft = applyAiExpenseAnswer(draft, '2026-06-15', [])
draft = applyAiExpenseAnswer(draft, '京东', [])
draft = applyAiExpenseAnswer(draft, '320元', [])
draft = applyAiExpenseAnswer(draft, '', [{ name: '发票.pdf' }])
assert.deepEqual(draft.values.attachment_names, ['发票.pdf'])
assert.ok(isAiExpenseDraftComplete(draft))
})
test('step prompt names the type and the current field', () => {
const draft = createAiExpenseDraft('transport', '交通费')
const prompt = buildAiExpenseStepPrompt(draft)
assert.match(prompt, /交通费/)
assert.match(prompt, /事由/)
})
test('summary lists every filled field and the linked application', () => {
let draft = createAiExpenseDraft('transport', '交通费')
draft = {
...draft,
applicationClaim: {
application_claim_no: 'AP-202606-001',
application_reason: '送客户去机场',
application_business_time: '2026-06-15',
application_location: '公司至机场'
}
}
draft = applyAiExpenseAnswer(draft, '送客户去机场', [])
draft = applyAiExpenseAnswer(draft, '2026-06-15', [])
draft = applyAiExpenseAnswer(draft, '公司至机场', [])
draft = applyAiExpenseAnswer(draft, '85元', [])
draft = applyAiExpenseAnswer(draft, '稍后上传', [])
const summary = buildAiExpenseSummary(draft)
assert.match(summary, /交通费/)
assert.match(summary, /AP-202606-001/)
assert.match(summary, /85元/)
})
test('buildAiExpenseDraftPrefillValues maps task ontology fields onto draft fields', () => {
const values = buildAiExpenseDraftPrefillValues({
expense_type: 'meal',
amount: '2000元',
time_range: '昨天',
reason: '客户招待',
location: '上海',
unrelated_field: 'ignore me'
})
assert.equal(values.amount, '2000元')
assert.equal(values.time_range, '昨天')
assert.equal(values.reason, '客户招待')
assert.equal(values.location, '上海')
assert.equal(values.unrelated_field, undefined)
})
test('createAiExpenseDraft with prefillValues skips already filled steps', () => {
const draft = createAiExpenseDraft('meal', '业务招待费', {
amount: '2000元',
reason: '客户招待'
})
// reason 已填,跳到下一个未填字段 time_range
assert.equal(draft.values.amount, '2000元')
assert.equal(draft.values.reason, '客户招待')
assert.equal(draft.stepKey, 'time_range')
})
test('createAiExpenseDraft with all prefillValues lands on summary', () => {
const draft = createAiExpenseDraft('meal', '业务招待费', {
reason: '客户招待',
time_range: '昨天',
location: '上海',
amount: '2000元',
attachments: '稍后上传'
})
assert.ok(isAiExpenseDraftComplete(draft))
})