2026-06-22 15:55:59 +08:00
|
|
|
|
import assert from 'node:assert/strict'
|
|
|
|
|
|
import test from 'node:test'
|
|
|
|
|
|
|
|
|
|
|
|
import { buildInlineApplicationPreview } from '../src/composables/workbenchAiMode/workbenchAiApplicationPreviewModel.js'
|
|
|
|
|
|
import { buildStewardSuggestedActions } from '../src/views/scripts/stewardPlanModel.js'
|
|
|
|
|
|
import { useWorkbenchAiActionRouter } from '../src/composables/workbenchAiMode/useWorkbenchAiActionRouter.js'
|
2026-06-24 10:42:50 +08:00
|
|
|
|
import {
|
|
|
|
|
|
CONTINUE_REIMBURSEMENT_DRAFT_ACTION,
|
|
|
|
|
|
CREATE_STANDALONE_REIMBURSEMENT_DRAFT_ACTION
|
|
|
|
|
|
} from '../src/views/scripts/travelReimbursementAssociationGateModel.js'
|
2026-06-22 15:55:59 +08:00
|
|
|
|
|
|
|
|
|
|
test('workbench steward application confirmation opens inline application preview directly', () => {
|
|
|
|
|
|
const [action] = buildStewardSuggestedActions({
|
|
|
|
|
|
plan_id: 'steward-plan-ready-application',
|
|
|
|
|
|
plan_status: 'ready',
|
|
|
|
|
|
tasks: [
|
|
|
|
|
|
{
|
|
|
|
|
|
task_id: 'task-application-beijing',
|
|
|
|
|
|
task_type: 'expense_application',
|
|
|
|
|
|
title: '费用申请 2026-06-23 北京',
|
|
|
|
|
|
summary: '明天前往北京出差3天,支撑客户现场实施。',
|
|
|
|
|
|
assigned_agent: 'application_assistant',
|
|
|
|
|
|
ontology_fields: {
|
|
|
|
|
|
expense_type: 'travel',
|
|
|
|
|
|
time_range: '2026-06-23 至 2026-06-25',
|
|
|
|
|
|
location: '北京',
|
|
|
|
|
|
days: '3天',
|
|
|
|
|
|
reason: '支撑客户现场实施'
|
|
|
|
|
|
},
|
|
|
|
|
|
missing_fields: ['transport_mode']
|
|
|
|
|
|
}
|
|
|
|
|
|
],
|
|
|
|
|
|
confirmation_groups: [
|
|
|
|
|
|
{
|
|
|
|
|
|
confirmation_id: 'confirm-application-beijing',
|
|
|
|
|
|
action_type: 'confirm_create_application',
|
|
|
|
|
|
target_task_id: 'task-application-beijing'
|
|
|
|
|
|
}
|
|
|
|
|
|
]
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
let previewPayload = null
|
|
|
|
|
|
let fallbackConversationStarted = false
|
|
|
|
|
|
const router = useWorkbenchAiActionRouter({
|
|
|
|
|
|
aiExpenseDraft: { value: null },
|
|
|
|
|
|
applicationFlow: {
|
|
|
|
|
|
isInlineSuggestedActionDisabled: () => false,
|
|
|
|
|
|
executeInlineApplicationPreviewAction: () => {}
|
|
|
|
|
|
},
|
|
|
|
|
|
assistantDraft: { value: '' },
|
|
|
|
|
|
attachmentFlow: {
|
|
|
|
|
|
confirmAiAttachmentAssociation: () => {}
|
|
|
|
|
|
},
|
|
|
|
|
|
emit: () => {},
|
|
|
|
|
|
expenseFlow: {
|
|
|
|
|
|
linkAiExpenseApplication: () => {},
|
|
|
|
|
|
pushInlineExpenseSceneSelectionPrompt: () => {},
|
|
|
|
|
|
startAiApplicationPreviewFromAction: (payload) => {
|
|
|
|
|
|
previewPayload = payload
|
|
|
|
|
|
},
|
|
|
|
|
|
startAiExpenseDraft: () => {}
|
|
|
|
|
|
},
|
|
|
|
|
|
focusAiModeInput: () => {},
|
|
|
|
|
|
hasInlineAttachmentOcrDetails: () => false,
|
|
|
|
|
|
resolveLatestInlineUserPrompt: () => '',
|
|
|
|
|
|
selectedFiles: { value: [] },
|
|
|
|
|
|
startInlineConversation: () => {
|
|
|
|
|
|
fallbackConversationStarted = true
|
|
|
|
|
|
},
|
|
|
|
|
|
toast: () => {},
|
|
|
|
|
|
toggleInlineAttachmentOcrDetails: () => {}
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
router.handleInlineSuggestedAction(action)
|
|
|
|
|
|
|
|
|
|
|
|
assert.equal(fallbackConversationStarted, false)
|
|
|
|
|
|
assert.equal(previewPayload?.flow_id, 'travel_application')
|
|
|
|
|
|
assert.equal(previewPayload?.expense_type, 'travel')
|
|
|
|
|
|
assert.equal(previewPayload?.expense_type_label, '差旅费')
|
|
|
|
|
|
assert.match(previewPayload?.carry_text || '', /支撑客户现场实施/)
|
|
|
|
|
|
|
|
|
|
|
|
const preview = buildInlineApplicationPreview(previewPayload.expense_type_label, previewPayload.carry_text, {
|
|
|
|
|
|
name: '测试用户',
|
|
|
|
|
|
departmentName: '交付部',
|
|
|
|
|
|
position: '实施顾问',
|
|
|
|
|
|
managerName: '张经理',
|
|
|
|
|
|
grade: 'P5'
|
|
|
|
|
|
})
|
|
|
|
|
|
assert.equal(preview.fields.time, '2026-06-23 至 2026-06-25')
|
|
|
|
|
|
assert.equal(preview.fields.location, '北京')
|
|
|
|
|
|
assert.equal(preview.fields.reason, '支撑客户现场实施')
|
|
|
|
|
|
assert.equal(preview.fields.days, '3天')
|
|
|
|
|
|
assert.equal(preview.fields.transportMode, '')
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
test('workbench reimbursement skip link action opens new reimbursement flow', () => {
|
|
|
|
|
|
let sceneSelectionPayload = null
|
|
|
|
|
|
let fallbackConversationStarted = false
|
|
|
|
|
|
const router = useWorkbenchAiActionRouter({
|
|
|
|
|
|
aiExpenseDraft: { value: null },
|
|
|
|
|
|
applicationFlow: {
|
|
|
|
|
|
isInlineSuggestedActionDisabled: () => false,
|
|
|
|
|
|
executeInlineApplicationPreviewAction: () => {}
|
|
|
|
|
|
},
|
|
|
|
|
|
assistantDraft: { value: '' },
|
|
|
|
|
|
attachmentFlow: {
|
|
|
|
|
|
confirmAiAttachmentAssociation: () => {}
|
|
|
|
|
|
},
|
|
|
|
|
|
emit: () => {},
|
|
|
|
|
|
expenseFlow: {
|
|
|
|
|
|
linkAiExpenseApplication: () => {},
|
|
|
|
|
|
pushInlineExpenseSceneSelectionPrompt: (sourceText, label) => {
|
|
|
|
|
|
sceneSelectionPayload = { sourceText, label }
|
|
|
|
|
|
},
|
|
|
|
|
|
startAiApplicationPreviewFromAction: () => {},
|
|
|
|
|
|
startAiExpenseDraft: () => {}
|
|
|
|
|
|
},
|
|
|
|
|
|
focusAiModeInput: () => {},
|
|
|
|
|
|
hasInlineAttachmentOcrDetails: () => false,
|
|
|
|
|
|
resolveLatestInlineUserPrompt: () => '',
|
|
|
|
|
|
selectedFiles: { value: [] },
|
|
|
|
|
|
startInlineConversation: () => {
|
|
|
|
|
|
fallbackConversationStarted = true
|
|
|
|
|
|
},
|
|
|
|
|
|
toast: () => {},
|
|
|
|
|
|
toggleInlineAttachmentOcrDetails: () => {}
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
router.handleInlineSuggestedAction({
|
|
|
|
|
|
label: '不关联,单独新建报销单',
|
|
|
|
|
|
action_type: 'skip_required_application_link',
|
|
|
|
|
|
payload: {
|
|
|
|
|
|
original_message: '我要报销'
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
assert.equal(fallbackConversationStarted, false)
|
|
|
|
|
|
assert.deepEqual(sceneSelectionPayload, {
|
|
|
|
|
|
sourceText: '我要报销',
|
|
|
|
|
|
label: '不关联,单独新建报销单'
|
|
|
|
|
|
})
|
|
|
|
|
|
})
|
2026-06-24 10:42:50 +08:00
|
|
|
|
|
|
|
|
|
|
test('workbench draft continuation action asks for attachments or description', () => {
|
|
|
|
|
|
let continuationPayload = null
|
|
|
|
|
|
let fallbackConversationStarted = false
|
|
|
|
|
|
const router = useWorkbenchAiActionRouter({
|
|
|
|
|
|
aiExpenseDraft: { value: null },
|
|
|
|
|
|
applicationFlow: {
|
|
|
|
|
|
isInlineSuggestedActionDisabled: () => false,
|
|
|
|
|
|
executeInlineApplicationPreviewAction: () => {}
|
|
|
|
|
|
},
|
|
|
|
|
|
assistantDraft: { value: '' },
|
|
|
|
|
|
attachmentFlow: {
|
|
|
|
|
|
confirmAiAttachmentAssociation: () => {}
|
|
|
|
|
|
},
|
|
|
|
|
|
emit: () => {},
|
|
|
|
|
|
expenseFlow: {
|
|
|
|
|
|
linkAiExpenseApplication: () => {},
|
|
|
|
|
|
promptAiReimbursementDraftContinuation: (payload) => {
|
|
|
|
|
|
continuationPayload = payload
|
|
|
|
|
|
},
|
|
|
|
|
|
promptStandaloneReimbursementDraftCreation: () => {},
|
|
|
|
|
|
pushInlineExpenseSceneSelectionPrompt: () => {},
|
|
|
|
|
|
startAiApplicationPreviewFromAction: () => {},
|
|
|
|
|
|
startAiExpenseDraft: () => {},
|
|
|
|
|
|
startAiReimbursementAssociationGate: () => {}
|
|
|
|
|
|
},
|
|
|
|
|
|
focusAiModeInput: () => {},
|
|
|
|
|
|
hasInlineAttachmentOcrDetails: () => false,
|
|
|
|
|
|
resolveLatestInlineUserPrompt: () => '',
|
|
|
|
|
|
selectedFiles: { value: [] },
|
|
|
|
|
|
startInlineConversation: () => {
|
|
|
|
|
|
fallbackConversationStarted = true
|
|
|
|
|
|
},
|
|
|
|
|
|
toast: () => {},
|
|
|
|
|
|
toggleInlineAttachmentOcrDetails: () => {}
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
router.handleInlineSuggestedAction({
|
|
|
|
|
|
label: '继续关联草稿 RE-202606-010',
|
|
|
|
|
|
action_type: CONTINUE_REIMBURSEMENT_DRAFT_ACTION,
|
|
|
|
|
|
payload: {
|
|
|
|
|
|
claim_id: 'draft-travel-1',
|
|
|
|
|
|
claim_no: 'RE-202606-010',
|
|
|
|
|
|
original_message: '我要报销'
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
assert.equal(fallbackConversationStarted, false)
|
|
|
|
|
|
assert.deepEqual(continuationPayload, {
|
|
|
|
|
|
claim_id: 'draft-travel-1',
|
|
|
|
|
|
claim_no: 'RE-202606-010',
|
|
|
|
|
|
original_message: '我要报销'
|
|
|
|
|
|
})
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
test('workbench standalone draft action asks before creating a new reimbursement draft', () => {
|
|
|
|
|
|
let standalonePrompt = null
|
|
|
|
|
|
let fallbackConversationStarted = false
|
|
|
|
|
|
const router = useWorkbenchAiActionRouter({
|
|
|
|
|
|
aiExpenseDraft: { value: null },
|
|
|
|
|
|
applicationFlow: {
|
|
|
|
|
|
isInlineSuggestedActionDisabled: () => false,
|
|
|
|
|
|
executeInlineApplicationPreviewAction: () => {}
|
|
|
|
|
|
},
|
|
|
|
|
|
assistantDraft: { value: '' },
|
|
|
|
|
|
attachmentFlow: {
|
|
|
|
|
|
confirmAiAttachmentAssociation: () => {}
|
|
|
|
|
|
},
|
|
|
|
|
|
emit: () => {},
|
|
|
|
|
|
expenseFlow: {
|
|
|
|
|
|
linkAiExpenseApplication: () => {},
|
|
|
|
|
|
promptAiReimbursementDraftContinuation: () => {},
|
|
|
|
|
|
promptStandaloneReimbursementDraftCreation: (sourceText, label) => {
|
|
|
|
|
|
standalonePrompt = { sourceText, label }
|
|
|
|
|
|
},
|
|
|
|
|
|
pushInlineExpenseSceneSelectionPrompt: () => {},
|
|
|
|
|
|
startAiApplicationPreviewFromAction: () => {},
|
|
|
|
|
|
startAiExpenseDraft: () => {},
|
|
|
|
|
|
startAiReimbursementAssociationGate: () => {}
|
|
|
|
|
|
},
|
|
|
|
|
|
focusAiModeInput: () => {},
|
|
|
|
|
|
hasInlineAttachmentOcrDetails: () => false,
|
|
|
|
|
|
resolveLatestInlineUserPrompt: () => '',
|
|
|
|
|
|
selectedFiles: { value: [] },
|
|
|
|
|
|
startInlineConversation: () => {
|
|
|
|
|
|
fallbackConversationStarted = true
|
|
|
|
|
|
},
|
|
|
|
|
|
toast: () => {},
|
|
|
|
|
|
toggleInlineAttachmentOcrDetails: () => {}
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
router.handleInlineSuggestedAction({
|
|
|
|
|
|
label: '独立新建报销单',
|
|
|
|
|
|
action_type: CREATE_STANDALONE_REIMBURSEMENT_DRAFT_ACTION,
|
|
|
|
|
|
payload: {
|
|
|
|
|
|
original_message: '我要报销'
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
assert.equal(fallbackConversationStarted, false)
|
|
|
|
|
|
assert.deepEqual(standalonePrompt, {
|
|
|
|
|
|
sourceText: '我要报销',
|
|
|
|
|
|
label: '独立新建报销单'
|
|
|
|
|
|
})
|
|
|
|
|
|
})
|