import assert from 'node:assert/strict' import { readFileSync } from 'node:fs' import test from 'node:test' import { fileURLToPath } from 'node:url' import { buildReviewPlainFollowupCopy } from '../src/views/scripts/travelReimbursementReviewModel.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 reimbursementService = readFileSync( fileURLToPath(new URL('../src/services/reimbursements.js', import.meta.url)), 'utf8' ) const reviewActionsScript = readFileSync( fileURLToPath(new URL('../src/views/scripts/useTravelReimbursementReviewActions.js', import.meta.url)), 'utf8' ) const reviewDrawerScript = readFileSync( fileURLToPath(new URL('../src/views/scripts/useTravelReimbursementReviewDrawer.js', import.meta.url)), 'utf8' ) const submitComposerScript = readFileSync( fileURLToPath(new URL('../src/views/scripts/useTravelReimbursementSubmitComposer.js', import.meta.url)), 'utf8' ) const attachmentsScript = readFileSync( fileURLToPath(new URL('../src/views/scripts/useTravelReimbursementAttachments.js', import.meta.url)), 'utf8' ) test('review drawer tools expose the default review tab before conditional document and risk tabs', () => { assert.match(createViewTemplate, /v-if="activeReviewPayload && reviewOverviewDrawerAvailable"[\s\S]*title="报销识别核对"[\s\S]*@click="switchToReviewOverviewDrawer"/) assert.match(createViewTemplate, /v-if="activeReviewPayload && reviewDocumentDrawerAvailable"[\s\S]*title="单据识别"/) assert.match(createViewTemplate, /v-if="activeReviewPayload && reviewRiskDrawerAvailable"[\s\S]*title="显示风险"/) assert.match(createViewTemplate, /title="调用流程"/) assert.ok( createViewTemplate.indexOf('title="报销识别核对"') < createViewTemplate.indexOf('title="单据识别"'), 'default review button should be placed before the document recognition button' ) }) test('review drawer tool buttons switch modes instead of toggling the active mode closed', () => { assert.match(createViewScript, /const isReviewOverviewDrawer = computed\(\(\) => reviewDrawerMode\.value === REVIEW_DRAWER_MODE_REVIEW\)/) assert.match(createViewScript, /function switchReviewDrawerMode\(mode\) \{[\s\S]*if \(reviewDrawerMode\.value === mode\) \{[\s\S]*return[\s\S]*\}/) assert.match(createViewScript, /function switchToReviewOverviewDrawer\(\) \{[\s\S]*switchReviewDrawerMode\(REVIEW_DRAWER_MODE_REVIEW\)/) assert.match(createViewScript, /function toggleReviewDocumentDrawer\(\) \{[\s\S]*switchReviewDrawerMode\(REVIEW_DRAWER_MODE_DOCUMENTS\)/) assert.match(createViewScript, /function toggleReviewRiskDrawer\(\) \{[\s\S]*switchReviewDrawerMode\(REVIEW_DRAWER_MODE_RISK\)/) assert.match(createViewScript, /function toggleReviewFlowDrawer\(\) \{[\s\S]*switchReviewDrawerMode\(REVIEW_DRAWER_MODE_FLOW\)/) assert.doesNotMatch(createViewScript, /REVIEW_DRAWER_MODE_DOCUMENTS\s*\?\s*REVIEW_DRAWER_MODE_REVIEW/) assert.doesNotMatch(createViewScript, /REVIEW_DRAWER_MODE_RISK\s*\?\s*REVIEW_DRAWER_MODE_REVIEW/) assert.doesNotMatch(createViewScript, /REVIEW_DRAWER_MODE_FLOW\s*\?\s*REVIEW_DRAWER_MODE_REVIEW/) }) test('review risk drawer lists risk briefs without score and posts details into the conversation', () => { const riskItemsBlock = createViewScript.match(/function buildReviewRiskItems\(reviewPayload\) \{[\s\S]*?\n\}\n\nfunction buildReviewRiskConversationText/) assert.ok(riskItemsBlock, 'risk item builder should be present') assert.doesNotMatch(createViewTemplate, /review-side-risk-score/) assert.doesNotMatch(createViewTemplate, /风险评分/) assert.doesNotMatch(createViewTemplate, /暂无风险评分/) assert.doesNotMatch(createViewScript, /function buildReviewRiskScore/) assert.doesNotMatch(createViewScript, /const reviewRiskScore/) assert.doesNotMatch(riskItemsBlock[0], /\.slice\(0,\s*6\)/) assert.match(createViewScript, /const DEPRECATED_REVIEW_RISK_TITLE_KEYWORDS = \[[\s\S]*'历史报销画像'[\s\S]*'制度注意事项'/) assert.match( createViewScript, /function resolveReviewRiskBriefs\(reviewPayload\) \{[\s\S]*DEPRECATED_REVIEW_RISK_TITLE_KEYWORDS\.some/ ) assert.match( createViewTemplate, /class="review-side-risk-item"[\s\S]*@click="appendReviewRiskBriefToConversation\(item\)"/ ) assert.doesNotMatch(createViewTemplate, /\{\{\s*item\.levelLabel\s*\}\}/) assert.match(createViewTemplate, /class="review-side-risk-icon" :title="item\.levelLabel"/) assert.match(createViewScript, /medium:\s*\{[\s\S]*label:\s*'中风险'/) assert.match(createViewScript, /low:\s*\{[\s\S]*label:\s*'低风险'/) assert.match(createViewScript, /function normalizeReviewRiskTitle/) assert.match(createViewScript, /\.replace\(\/AI\\s\*预审/) assert.match(createViewScript, /\.replace\(\/\(高风险\|中风险\|低风险\)\/g,\s*''\)/) assert.match(createViewScript, /sourceLabel:\s*meta\.label/) assert.doesNotMatch(createViewScript, /normalizedTitle\.includes\('AI预审'\)/) assert.match(createViewScript, /metaTone:\s*item\.level \|\| 'low'/) assert.doesNotMatch(createViewTemplate, /@click="openReviewRiskDetail\(item\)"/) assert.doesNotMatch(createViewTemplate, /review-risk-detail-modal/) assert.doesNotMatch(createViewScript, /reviewRiskDetailDialog/) assert.doesNotMatch(createViewScript, /function openReviewRiskDetail/) assert.match( createViewScript, /function appendReviewRiskBriefToConversation\(item\) \{[\s\S]*messages\.value\.push\(createMessage\('assistant'/ ) assert.match(createViewScript, /function buildReviewRiskConversationText\(item, detailTarget = \{\}\)/) assert.match(createViewScript, /function resolveReviewRiskDetailTarget\(\) \{[\s\S]*router\.resolve\(\{[\s\S]*name: 'app-request-detail'/) assert.match(createViewScript, /进入 \$\{claimNo\} 详情重新填写/) assert.match(createViewTemplate, /class="expense-query-risk-row"[\s\S]*appendExpenseQueryRiskToConversation\(record, risk\)/) assert.match(createViewScript, /function appendExpenseQueryRiskToConversation\(record, risk\) \{[\s\S]*进入 \$\{claimNo\} 详情重新填写/) }) test('review drawer default mode is scoped by the current action and travel overview uses travel-specific fields', () => { assert.match(reviewDrawerScript, /activeReviewPanelScope/) assert.match(reviewDrawerScript, /const reviewOverviewDrawerAvailable = computed\(\(\) => normalizedReviewPanelScope\.value === 'overview'\)/) assert.match(reviewDrawerScript, /scope === 'documents' && hasDocuments[\s\S]*REVIEW_DRAWER_MODE_DOCUMENTS/) assert.match(reviewDrawerScript, /scope === 'risk' && hasRisks[\s\S]*REVIEW_DRAWER_MODE_RISK/) assert.match(reviewDrawerScript, /scope === 'overview'[\s\S]*REVIEW_DRAWER_MODE_REVIEW/) assert.match(createViewScript, /function normalizeReviewPanelScope\(scope\)/) assert.match(createViewScript, /canExposeReviewPanelScope\(item\.reviewPanelScope\)/) assert.match(createViewScript, /currentInsight\.value\.intent === 'agent' && agent[\s\S]*return null/) assert.match(createViewScript, /function isTravelReviewPayload\(reviewPayload/) assert.match(createViewScript, /function resolveReviewTravelTransportType\(reviewPayload/) assert.match(createViewScript, /label: '交通类型'[\s\S]*modelKey: 'transport_type'/) assert.match(createViewScript, /label: '酒店名称'[\s\S]*modelKey: 'merchant_name'/) assert.match(createViewScript, /label: '出差事宜'[\s\S]*editor: 'textarea'[\s\S]*wide: true/) assert.match(createViewTemplate, /item\.editor === 'textarea'[\s\S]*