Files
X-Financial/web/tests/expense-application-submit-rich-confirm.test.mjs
Codex 8b952c9a26 refactor(travel): split reimbursement create workflow
完整修改内容:

- 拆分 TravelReimbursementCreateView:提取审核面板纯模型、消息操作、建议动作处理、生命周期 watcher/UI 映射、小财管家运行时、续办流程和运行时文本模型,减少主组件继续堆叠业务分支。
- 调整申请预览链路:新增本地申请意图 gate,完善复杂差旅申请的大模型复核判断、交通方式缺失/候选识别、规则中心交通费用预估合并和申请冲突处理。
- 优化小财管家流程:抽出 steward typewriter 分段策略,避免 Markdown 表格逐字闪烁;补齐跨助手 carry、字段补齐续办、文本确认提交和行程规划推荐动作。
- 调整消息与样式:移除申请预览日期 chip 样式,收敛申请卡片/报销草稿消息的展示与复制、朗读、反馈入口逻辑。
- 更新测试:将源码锚点迁移到新模块,覆盖申请预览、提交确认、小财管家续办、引导流和审核抽屉相关断言。

验证:

- node --check web/src/views/scripts/TravelReimbursementCreateView.js 及新增拆分模块
- npm --prefix web run build
- node --test web/tests/expense-application-fast-preview.test.mjs web/tests/expense-application-submit-rich-confirm.test.mjs web/tests/travel-reimbursement-guided-flow.test.mjs

说明:

- 后端/规则/容器配置/Audit 页面等工作区已有改动未纳入本提交。
- 容器内后端定向 pytest 曾运行 timeout 180s /tmp/x-financial-server-venv/bin/pytest -q <相关后端测试>,180 秒超时且超时前已有失败标记,未作为通过项记录。
- TravelReimbursementCreateView 当前仍超过 800 行,后续仍需继续拆分;本提交先把新增职责模块控制在 800 行内,阻止主类/主模块继续膨胀。
2026-06-13 14:53:23 +00:00

69 lines
3.4 KiB
JavaScript

import assert from 'node:assert/strict'
import { readFileSync } from 'node:fs'
import test from 'node:test'
import { fileURLToPath } from 'node:url'
import { renderMarkdown } from '../src/utils/markdown.js'
const createViewTemplate = readFileSync(
fileURLToPath(new URL('../src/views/TravelReimbursementCreateView.vue', import.meta.url)),
'utf8'
)
const createViewScript = readFileSync(
fileURLToPath(new URL('../src/views/scripts/TravelReimbursementCreateView.js', import.meta.url)),
'utf8'
)
const suggestedActionsScript = readFileSync(
fileURLToPath(new URL('../src/views/scripts/useTravelReimbursementSuggestedActions.js', import.meta.url)),
'utf8'
)
const stewardRuntimeScript = readFileSync(
fileURLToPath(new URL('../src/views/scripts/useTravelReimbursementStewardRuntime.js', import.meta.url)),
'utf8'
)
const messageItemStyles = readFileSync(
fileURLToPath(new URL('../src/assets/styles/components/travel-reimbursement-message-item.css', import.meta.url)),
'utf8'
)
test('expense application submit uses rich text link and confirm dialog', () => {
const copy = '请确认上述的信息是否填写正确?如果准确无误,点击 [确认](#application-submit) 进入审批环节。'
const rendered = renderMarkdown(copy)
assert.match(copy, /请确认上述的信息是否填写正确/)
assert.match(copy, /进入审批环节/)
assert.match(
rendered,
/<a href="#application-submit" class="markdown-action-link markdown-action-link-confirm">确认<\/a>/
)
assert.match(messageItemStyles, /\.application-preview-footer \{[\s\S]*margin-top: 48px;/)
assert.match(messageItemStyles, /\.message-answer-markdown :deep\(\.markdown-action-link\) \{[\s\S]*text-decoration: underline;/)
assert.match(createViewTemplate, /:open="applicationSubmitConfirmDialog\.open"/)
assert.match(createViewTemplate, /title="确认提交当前费用申请?"/)
assert.match(createViewTemplate, /description="提交后申请将进入领导审核流程,请确认关键申请信息和预计费用已经核对无误。"/)
assert.match(createViewTemplate, /@confirm="confirmApplicationSubmit"/)
assert.match(createViewScript, /const APPLICATION_SUBMIT_HREF = '#application-submit'/)
assert.match(
createViewScript,
/href === APPLICATION_SUBMIT_HREF[\s\S]*openApplicationSubmitConfirm\(message\)/
)
assert.match(
stewardRuntimeScript,
/async function confirmApplicationSubmit\(options = \{\}\)[\s\S]*const applicationSubmitText[\s\S]*rawText: applicationSubmitText[\s\S]*systemGenerated: true[\s\S]*skipScopeGuard: true/
)
assert.match(
stewardRuntimeScript,
/applicationSubmitConfirmDialog\.value = \{[\s\S]*open: false,[\s\S]*message: null[\s\S]*\}[\s\S]*const payload = await submitComposer/
)
assert.match(
stewardRuntimeScript,
/emit\('draft-saved', \{[\s\S]*status: 'submitted'[\s\S]*documentType: 'application'/
)
assert.match(stewardRuntimeScript, /buildTravelPlanningNudgeMessage\(applicationPreview, draftPayload\)/)
assert.match(stewardRuntimeScript, /buildTravelPlanningSuggestedActions\(applicationPreview, draftPayload\)/)
assert.match(stewardRuntimeScript, /meta:\s*\['行程规划推荐'\]/)
assert.match(suggestedActionsScript, /TRAVEL_PLANNING_ACTION_GENERATE/)
assert.match(suggestedActionsScript, /buildTravelPlanningRecommendation\(sourcePreview, sourceDraftPayload\)/)
assert.match(suggestedActionsScript, /TRAVEL_PLANNING_ACTION_SKIP/)
})