import assert from 'node:assert/strict' import { readFileSync } from 'node:fs' import test from 'node:test' import { fileURLToPath } from 'node:url' const detailTemplate = readFileSync( fileURLToPath(new URL('../src/views/TravelRequestDetailView.vue', import.meta.url)), 'utf8' ) const detailScript = readFileSync( fileURLToPath(new URL('../src/views/scripts/TravelRequestDetailView.js', import.meta.url)), 'utf8' ) const detailStyles = readFileSync( fileURLToPath(new URL('../src/assets/styles/views/travel-request-detail-view.css', import.meta.url)), 'utf8' ) const reimbursementService = readFileSync( fileURLToPath(new URL('../src/services/reimbursements.js', import.meta.url)), 'utf8' ) function extractFunction(source, name) { const signatureIndex = source.indexOf(`function ${name}(`) assert.notEqual(signatureIndex, -1, `${name} should exist`) const bodyStart = source.indexOf('{', signatureIndex) assert.notEqual(bodyStart, -1, `${name} should have a body`) let depth = 0 for (let index = bodyStart; index < source.length; index += 1) { const char = source[index] if (char === '{') { depth += 1 } else if (char === '}') { depth -= 1 if (depth === 0) { return source.slice(signatureIndex, index + 1) } } } assert.fail(`${name} body should be closed`) } test('approval-mode detail collects leader opinion and confirms approval before API call', () => { assert.match(detailScript, /approvalMode:/) assert.match(detailScript, /const leaderOpinion = ref\(''\)/) assert.match(detailScript, /const approveConfirmDialogOpen = ref\(false\)/) assert.match(detailScript, /const canApproveRequest = computed/) assert.match(detailScript, /canApproveLeaderExpenseClaims/) assert.match(detailScript, /isFinanceApprovalStage/) assert.match(detailScript, /approvalOpinionTitle/) assert.match(detailScript, /approvalConfirmDescription/) assert.match(detailScript, /approvalNextStage/) assert.match(detailScript, /showApplicationLeaderOpinionInput/) assert.match(detailScript, /const requiresApprovalOpinion = computed\(\(\) => isDirectManagerApprovalStage\.value\)/) assert.match(detailScript, /buildLeaderApprovalInfo/) assert.match(detailScript, /resolveGeneratedDraftClaimNo/) assert.match(detailScript, /approveActionLabel/) assert.match(detailScript, /requiresApprovalOpinion\.value && !leaderOpinion\.value\.trim\(\)/) assert.match(detailScript, /请先填写领导意见,填写后才能确认审核。/) assert.match(detailScript, /approveExpenseClaim\(request\.value\.claimId, \{[\s\S]*opinion: leaderOpinion\.value\.trim\(\)/) assert.match(detailScript, /报销草稿 \$\{generatedDraftClaimNo\} 已生成/) assert.match(detailTemplate, /v-if="showLeaderApprovalPanel"/) assert.match(detailTemplate, /v-if="showApplicationLeaderOpinion"/) assert.match(detailTemplate, /class="application-leader-opinion"/) assert.match(detailTemplate, /领导意见/) assert.match(detailTemplate, /\{\{ approvalOpinionTitle \}\}/) assert.match(detailTemplate, /v-model="leaderOpinion"/) assert.match(detailTemplate, /maxlength="500"\s+:required="requiresApprovalOpinion"/) assert.match(detailTemplate, /:placeholder="approvalOpinionPlaceholder"/) assert.match(detailTemplate, /@click="handleApproveRequest"/) assert.match(detailTemplate, /\{\{ approveBusy \? approveBusyLabel : approveActionLabel \}\}/) assert.match(detailTemplate, /:open="approveConfirmDialogOpen"/) assert.match(detailTemplate, /:badge="approvalConfirmBadge"/) assert.match(detailTemplate, /:description="approvalConfirmDescription"/) assert.match(detailTemplate, /:confirm-text="approveConfirmText"/) assert.match(detailTemplate, /:busy-text="approveBusyText"/) assert.match(detailTemplate, /\{\{ approvalNextStage \}\}/) assert.match(detailTemplate, /@confirm="confirmApproveRequest"/) assert.match(detailTemplate, /:description="returnDialogDescription"/) const handleApproveRequest = extractFunction(detailScript, 'handleApproveRequest') const confirmApproveRequest = extractFunction(detailScript, 'confirmApproveRequest') assert.doesNotMatch(handleApproveRequest, /approveExpenseClaim/) assert.match(confirmApproveRequest, /approveExpenseClaim/) assert.match(detailStyles, /\.detail-card-title-with-icon \{[\s\S]*display: inline-flex;[\s\S]*align-items: center;[\s\S]*gap: 8px;/) assert.match(detailStyles, /\.detail-card-title-with-icon i \{[\s\S]*font-size: 18px;[\s\S]*line-height: 1;/) assert.match(detailStyles, /\.application-leader-opinion-head span \{[\s\S]*display: inline-flex;[\s\S]*align-items: center;[\s\S]*gap: 8px;/) assert.match(reimbursementService, /export function approveExpenseClaim\(claimId, payload = \{\}\)/) assert.match(reimbursementService, /\/approve/) })