refactor: enforce 800 line source limits
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import { execFileSync } from 'node:child_process'
|
||||
import { readFileSync, statSync } from 'node:fs'
|
||||
import { readdirSync, readFileSync, statSync } from 'node:fs'
|
||||
import test from 'node:test'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
|
||||
@@ -155,6 +155,14 @@ print(json.dumps({
|
||||
const appShell = readSource('../src/views/AppShellRouteView.vue')
|
||||
const workbenchView = readSource('../src/views/PersonalWorkbenchView.vue')
|
||||
const aiMode = readSource('../src/components/business/PersonalWorkbenchAiMode.vue')
|
||||
const aiModeTemplate = readSource('../src/components/business/PersonalWorkbenchAiMode.template.html')
|
||||
const aiModeRuntimeDir = fileURLToPath(new URL('../src/composables/workbenchAiMode/', import.meta.url))
|
||||
const aiModeRuntime = readdirSync(aiModeRuntimeDir)
|
||||
.filter((file) => file.endsWith('.js'))
|
||||
.sort()
|
||||
.map((file) => readFileSync(new URL(`../src/composables/workbenchAiMode/${file}`, import.meta.url), 'utf8'))
|
||||
.join('\n')
|
||||
const aiModeSurface = `${aiMode}\n${aiModeTemplate}\n${aiModeRuntime}`
|
||||
const aiModeStyles = readSource('../src/assets/styles/components/personal-workbench-ai-mode.css')
|
||||
const workbenchViewStyles = readSource('../src/assets/styles/views/personal-workbench-view.css')
|
||||
const appStyles = readSource('../src/assets/styles/app.css')
|
||||
@@ -210,67 +218,106 @@ test('personal workbench view swaps the traditional dashboard with the AI mode s
|
||||
})
|
||||
|
||||
test('AI mode screen follows the approved reference structure', () => {
|
||||
assert.match(aiMode, /personal-workbench-ai-mode\.css/)
|
||||
assert.doesNotMatch(aiMode, /workbench-ai-mode-robot-bg\.png/)
|
||||
assert.match(aiMode, /workbench-ai-mode-orb-icon\.gif/)
|
||||
assert.match(aiMode, /<img[\s\S]*class="workbench-ai-orb__image"/)
|
||||
assert.match(aiMode, /小财管家/)
|
||||
assert.match(aiMode, /我是您的小财管家/)
|
||||
assert.match(aiMode, /今天我能帮您做点什么?/)
|
||||
assert.match(aiMode, /费用测算中,请稍等/)
|
||||
assert.match(aiMode, /rows="3"/)
|
||||
assert.match(aiMode, /workbench-ai-composer-toolbar/)
|
||||
assert.match(aiMode, /<article v-for="file in selectedFileCards"[\s\S]*class="workbench-ai-file-card"/)
|
||||
assert.match(aiMode, /:aria-label="`移除附件 \$\{file\.name\}`"/)
|
||||
assert.match(aiMode, /function removeAiModeFile\(fileKey\)/)
|
||||
assert.match(aiMode, /const selectedFileCards = computed/)
|
||||
assert.match(aiMode, /resolveAiComposerFileType\(file\)/)
|
||||
assert.match(aiMode, /AI_COMPOSER_FILE_TYPE_META = \{[\s\S]*pdf:\s*\{ label:\s*'PDF'/)
|
||||
assert.doesNotMatch(aiMode, /已选择 \{\{ selectedFiles\.length \}\} 份附件/)
|
||||
assert.match(aiMode, /Axiom Ultra 3\.1/)
|
||||
assert.match(aiMode, /mdi mdi-calendar-range/)
|
||||
assert.match(aiMode, /workbench-ai-date-popover/)
|
||||
assert.match(aiMode, /type="date"/)
|
||||
assert.doesNotMatch(aiMode, /mdi mdi-web/)
|
||||
assert.match(aiMode, /mdi mdi-microphone-outline/)
|
||||
assert.match(aiMode, /mdi mdi-arrow-up/)
|
||||
assert.match(aiMode, /快速开始/)
|
||||
assert.match(aiMode, /action-icon-wrapper/)
|
||||
assert.match(aiMode, /发起报销/)
|
||||
assert.match(aiMode, /查询预算/)
|
||||
assert.match(aiMode, /解释制度/)
|
||||
assert.match(aiMode, /催办审批/)
|
||||
assert.match(aiMode, /<Transition name="workbench-ai-panel-swap" mode="out-in" appear>/)
|
||||
assert.match(aiMode, /@submit\.prevent="submitAiModePrompt"/)
|
||||
assert.equal((aiMode.match(/type="submit"[\s\S]{0,160}class="workbench-ai-send-btn"/g) || []).length, 2)
|
||||
assert.match(aiMode, /class="workbench-ai-conversation"/)
|
||||
assert.match(aiMode, /class="workbench-ai-thread"[\s\S]*@scroll\.passive="handleInlineConversationScroll"/)
|
||||
assert.match(aiMode, /workbench-ai-answer-card/)
|
||||
assert.match(aiMode, /workbench-ai-answer-markdown/)
|
||||
assert.match(aiMode, /v-html="renderInlineConversationHtml\(message\.content\)"/)
|
||||
assert.match(aiMode, /workbench-ai-message-actions/)
|
||||
assert.match(aiMode, /workbench-ai-conversation-actions/)
|
||||
assert.match(aiMode, /scrollInlineConversationToTop/)
|
||||
assert.match(aiMode, /requestDeleteCurrentConversation/)
|
||||
assert.match(aiMode, /confirmDeleteConversation/)
|
||||
assert.match(aiMode, /workbench-ai-confirm-dialog/)
|
||||
assert.match(aiMode, /workbench-ai-thinking-toggle/)
|
||||
assert.match(aiMode, /小财业务思考/)
|
||||
assert.match(aiMode, /class="workbench-ai-thinking-expanded"/)
|
||||
assert.match(aiMode, /class="workbench-ai-thinking-collapse-btn"/)
|
||||
assert.match(aiMode, /class="workbench-ai-thinking-collapse-btn"[\s\S]*@click="toggleInlineThinking\(message\)"/)
|
||||
assert.doesNotMatch(aiMode, /:disabled="message\.pending"/)
|
||||
assert.match(aiMode, /isInlineThinkingExpanded/)
|
||||
assert.match(aiMode, /toggleInlineThinking/)
|
||||
assert.match(aiMode, /const thinkingCollapsedMessageIds = ref\(new Set\(\)\)/)
|
||||
assert.match(aiMode, /thinkingCollapsedMessageIds\.value\.has\(message\.id\)/)
|
||||
assert.match(aiMode, /nextCollapsedIds\.add\(message\.id\)/)
|
||||
assert.match(aiMode, /nextCollapsedIds\.delete\(message\.id\)/)
|
||||
assert.match(aiMode, /message\.pending && !hasInlineThinking\(message\)/)
|
||||
assert.doesNotMatch(aiMode, /小财管家正在思考/)
|
||||
assert.doesNotMatch(aiMode, /思考过程/)
|
||||
assert.doesNotMatch(aiMode, /message\.pending \?/)
|
||||
assert.match(aiMode, /继续和小财管家对话\.\.\./)
|
||||
assert.match(aiModeSurface, /personal-workbench-ai-mode\.css/)
|
||||
assert.doesNotMatch(aiModeSurface, /workbench-ai-mode-robot-bg\.png/)
|
||||
assert.match(aiModeSurface, /workbench-ai-mode-orb-icon\.gif/)
|
||||
assert.match(aiModeSurface, /<img[\s\S]*class="workbench-ai-orb__image"/)
|
||||
assert.match(aiModeSurface, /小财管家/)
|
||||
assert.match(aiModeSurface, /我是您的小财管家/)
|
||||
assert.match(aiModeSurface, /今天我能帮您做点什么?/)
|
||||
assert.match(aiModeSurface, /费用测算中,请稍等/)
|
||||
assert.match(aiModeSurface, /rows="3"/)
|
||||
assert.match(aiModeSurface, /workbench-ai-composer-toolbar/)
|
||||
assert.match(aiModeSurface, /<article v-for="file in selectedFileCards"[\s\S]*class="workbench-ai-file-card"/)
|
||||
assert.match(aiModeSurface, /:aria-label="`移除附件 \$\{file\.name\}`"/)
|
||||
assert.match(aiModeSurface, /function removeAiModeFile\(fileKey\)/)
|
||||
assert.match(aiModeSurface, /const selectedFileCards = computed/)
|
||||
assert.match(aiModeSurface, /resolveAiComposerFileType\(file\)/)
|
||||
assert.match(aiModeSurface, /AI_COMPOSER_FILE_TYPE_META = \{[\s\S]*pdf:\s*\{ label:\s*'PDF'/)
|
||||
assert.match(aiModeSurface, /import \{ collectReceiptFiles \} from '\.\.\/\.\.\/views\/scripts\/travelReimbursementAttachmentModel\.js'/)
|
||||
assert.match(aiModeSurface, /MAX_ATTACHMENTS,[\s\S]*mergeFilesWithLimit[\s\S]*travelReimbursementAttachmentModel\.js/)
|
||||
assert.match(aiModeSurface, /import \* as aiAttachmentAssociationModel from '\.\.\/\.\.\/utils\/aiAttachmentAssociationModel\.js'/)
|
||||
assert.match(aiModeSurface, /aiAttachmentAssociationModel\.resolveAiAttachmentAssociationMatch/)
|
||||
assert.match(aiModeSurface, /aiAttachmentAssociationModel\.buildAiAttachmentAssociationResultMessage/)
|
||||
assert.match(aiModeSurface, /syncExpenseClaimFilesToDraft/)
|
||||
assert.match(aiModeSurface, /import \{ recognizeOcrFiles \} from '\.\.\/\.\.\/services\/ocr\.js'/)
|
||||
assert.match(aiModeSurface, /const AI_ATTACHMENT_ASSOCIATION_CONFIRM_ACTION = 'confirm_ai_attachment_association'/)
|
||||
assert.match(aiModeSurface, /const AI_ATTACHMENT_OCR_DETAIL_ACTION = 'show_ai_attachment_ocr_details'/)
|
||||
assert.match(aiModeSurface, /function isLikelyReceiptAssociationFile\(file = \{\}\)/)
|
||||
assert.match(aiModeSurface, /function streamOrSetInlineAssistantContent\(messageId, content\)/)
|
||||
assert.match(aiModeSurface, /ai-trusted-html:start/)
|
||||
assert.match(aiModeSurface, /class="workbench-ai-ocr-detail-panel"/)
|
||||
assert.match(aiModeSurface, /附件识别明细/)
|
||||
assert.match(aiModeSurface, /attachmentOcrDetails: normalizeInlineAttachmentOcrDetails/)
|
||||
assert.match(aiModeSurface, /function buildInlineAttachmentOcrDetails\(collected = \{\}, files = \[\]\)/)
|
||||
assert.match(aiModeSurface, /function toggleInlineAttachmentOcrDetails\(message = \{\}, forceExpanded = null\)/)
|
||||
assert.match(aiModeSurface, /function resolveLegacyAiAttachmentAssociationPayload\(content = ''\)/)
|
||||
assert.match(aiModeSurface, /function hydrateInlineAttachmentAssociationSuggestedActions\(actions = \[\], content = ''\)/)
|
||||
assert.match(aiModeSurface, /label:\s*'确认自动关联'/)
|
||||
assert.match(aiModeSurface, /function shouldRunAiAttachmentAutoAssociation\(entry = \{\}, files = \[\], prompt = ''\)/)
|
||||
assert.match(aiModeSurface, /files\.every\(\(file\) => isLikelyReceiptAssociationFile\(file\)\)/)
|
||||
assert.match(aiModeSurface, /!shouldKeepAiAttachmentInAssistantReply\(prompt\)/)
|
||||
assert.match(aiModeSurface, /const aiAttachmentAssociationRuntime = new Map\(\)/)
|
||||
assert.match(aiModeSurface, /function findAiAttachmentAssociationRuntime\(options = \{\}\)/)
|
||||
assert.match(aiModeSurface, /resolveAiAttachmentAssociationClaimNo\(actionPayload\)/)
|
||||
assert.match(aiModeSurface, /if \(actionType === AI_ATTACHMENT_OCR_DETAIL_ACTION\)/)
|
||||
assert.match(aiModeSurface, /collectReceiptFiles\(\{[\s\S]*files,[\s\S]*recognizeOcrFiles[\s\S]*\}\)/)
|
||||
assert.match(aiModeSurface, /const claims = extractExpenseClaimItems\(claimsPayload\)/)
|
||||
assert.match(aiModeSurface, /aiAttachmentAssociationModel\.resolveAiAttachmentAssociationMatch\(claims, collected\.ocrDocuments\)/)
|
||||
assert.match(aiModeSurface, /aiAttachmentAssociationRuntime\.set\(associationId/)
|
||||
assert.match(aiModeSurface, /attachmentOcrDetails,\s*[\s\S]*includeOcrDetails: Boolean\(attachmentOcrDetails\)/)
|
||||
assert.match(aiModeSurface, /async function confirmAiAttachmentAssociation\(actionPayload = \{\}, sourceMessage = null\)/)
|
||||
assert.match(aiModeSurface, /syncExpenseClaimFilesToDraft\(\{[\s\S]*fetchExpenseClaimDetail,[\s\S]*createExpenseClaimItem,[\s\S]*uploadExpenseClaimItemAttachment/)
|
||||
assert.match(aiModeSurface, /if \(actionType === AI_ATTACHMENT_ASSOCIATION_CONFIRM_ACTION\)/)
|
||||
assert.match(aiModeSurface, /if \(shouldRunAiAttachmentAutoAssociation\(entry, files, cleanPrompt\)\) \{[\s\S]*requestAiAttachmentAssociationReply\(cleanPrompt, entry, files\)/)
|
||||
assert.match(aiModeSurface, /const fileMergeResult = mergeFilesWithLimit\(selectedFiles\.value, Array\.from\(event\.target\.files \|\| \[\]\), MAX_ATTACHMENTS\)/)
|
||||
assert.match(aiModeSurface, /selectedFiles\.value = fileMergeResult\.files/)
|
||||
assert.doesNotMatch(aiModeSurface, /selectedFiles\.value = Array\.from\(event\.target\.files \|\| \[\]\)\.slice\(0, 10\)/)
|
||||
assert.doesNotMatch(aiModeSurface, /已选择 \{\{ selectedFiles\.length \}\} 份附件/)
|
||||
assert.match(aiModeSurface, /Axiom Ultra 3\.1/)
|
||||
assert.match(aiModeSurface, /mdi mdi-calendar-range/)
|
||||
assert.match(aiModeSurface, /workbench-ai-date-popover/)
|
||||
assert.match(aiModeSurface, /type="date"/)
|
||||
assert.doesNotMatch(aiModeSurface, /mdi mdi-web/)
|
||||
assert.match(aiModeSurface, /mdi mdi-microphone-outline/)
|
||||
assert.match(aiModeSurface, /mdi mdi-arrow-up/)
|
||||
assert.match(aiModeSurface, /快速开始/)
|
||||
assert.match(aiModeSurface, /action-icon-wrapper/)
|
||||
assert.match(aiModeSurface, /发起报销/)
|
||||
assert.match(aiModeSurface, /查询预算/)
|
||||
assert.match(aiModeSurface, /解释制度/)
|
||||
assert.match(aiModeSurface, /催办审批/)
|
||||
assert.match(aiModeSurface, /<Transition name="workbench-ai-panel-swap" mode="out-in" appear>/)
|
||||
assert.match(aiModeSurface, /@submit\.prevent="submitAiModePrompt"/)
|
||||
assert.equal((aiModeSurface.match(/type="submit"[\s\S]{0,160}class="workbench-ai-send-btn"/g) || []).length, 2)
|
||||
assert.match(aiModeSurface, /class="workbench-ai-conversation"/)
|
||||
assert.match(aiModeSurface, /class="workbench-ai-thread"[\s\S]*@scroll\.passive="handleInlineConversationScroll"/)
|
||||
assert.match(aiModeSurface, /workbench-ai-answer-card/)
|
||||
assert.match(aiModeSurface, /workbench-ai-answer-markdown/)
|
||||
assert.match(aiModeSurface, /v-html="renderInlineConversationHtml\(message\.content\)"/)
|
||||
assert.match(aiModeSurface, /workbench-ai-message-actions/)
|
||||
assert.match(aiModeSurface, /workbench-ai-conversation-actions/)
|
||||
assert.match(aiModeSurface, /scrollInlineConversationToTop/)
|
||||
assert.match(aiModeSurface, /requestDeleteCurrentConversation/)
|
||||
assert.match(aiModeSurface, /confirmDeleteConversation/)
|
||||
assert.match(aiModeSurface, /workbench-ai-confirm-dialog/)
|
||||
assert.match(aiModeSurface, /workbench-ai-thinking-toggle/)
|
||||
assert.match(aiModeSurface, /小财业务思考/)
|
||||
assert.match(aiModeSurface, /class="workbench-ai-thinking-expanded"/)
|
||||
assert.match(aiModeSurface, /class="workbench-ai-thinking-collapse-btn"/)
|
||||
assert.match(aiModeSurface, /class="workbench-ai-thinking-collapse-btn"[\s\S]*@click="toggleInlineThinking\(message\)"/)
|
||||
assert.doesNotMatch(aiModeSurface, /:disabled="message\.pending"/)
|
||||
assert.match(aiModeSurface, /isInlineThinkingExpanded/)
|
||||
assert.match(aiModeSurface, /toggleInlineThinking/)
|
||||
assert.match(aiModeSurface, /const thinkingCollapsedMessageIds = ref\(new Set\(\)\)/)
|
||||
assert.match(aiModeSurface, /thinkingCollapsedMessageIds\.value\.has\(message\.id\)/)
|
||||
assert.match(aiModeSurface, /nextCollapsedIds\.add\(message\.id\)/)
|
||||
assert.match(aiModeSurface, /nextCollapsedIds\.delete\(message\.id\)/)
|
||||
assert.match(aiModeSurface, /message\.pending && !hasInlineThinking\(message\)/)
|
||||
assert.doesNotMatch(aiModeSurface, /小财管家正在思考/)
|
||||
assert.doesNotMatch(aiModeSurface, /思考过程/)
|
||||
assert.doesNotMatch(aiModeSurface, /message\.pending \?/)
|
||||
assert.match(aiModeSurface, /继续和小财管家对话\.\.\./)
|
||||
assert.match(aiModeStyles, /\.workbench-ai-answer-markdown :deep\(\.ai-document-card\)/)
|
||||
assert.match(aiModeStyles, /\.workbench-ai-answer-markdown :deep\(\.ai-document-query-summary\)/)
|
||||
assert.match(aiModeStyles, /\.workbench-ai-answer-markdown :deep\(\.ai-document-query-summary__scope\)/)
|
||||
@@ -295,40 +342,40 @@ test('AI mode screen follows the approved reference structure', () => {
|
||||
assert.match(aiModeStyles, /\.workbench-ai-answer-markdown :deep\(\.ai-html-action-link\)/)
|
||||
assert.match(aiModeStyles, /\.workbench-ai-answer-markdown :deep\(\.ai-html-table-wrap\)/)
|
||||
assert.match(aiModeStyles, /\.workbench-ai-answer-markdown :deep\(\.ai-html-image-frame\)/)
|
||||
assert.match(aiMode, /import \{ fetchSettings \} from '\.\.\/\.\.\/services\/settings\.js'/)
|
||||
assert.match(aiMode, /import \{ fetchStewardPlan, fetchStewardPlanStream \} from '\.\.\/\.\.\/services\/steward\.js'/)
|
||||
assert.match(aiMode, /import \{ useWorkbenchComposerDate \} from '\.\.\/\.\.\/composables\/useWorkbenchComposerDate\.js'/)
|
||||
assert.match(aiMode, /loadAiWorkbenchConversationHistory/)
|
||||
assert.match(aiMode, /saveAiWorkbenchConversation/)
|
||||
assert.match(aiMode, /deleteAiWorkbenchConversation/)
|
||||
assert.match(aiMode, /import \{ renderAiConversationHtml \} from '\.\.\/\.\.\/utils\/aiConversationHtmlRenderer\.js'/)
|
||||
assert.match(aiMode, /function renderInlineConversationHtml\(content\) \{[\s\S]*return renderAiConversationHtml\(content\)[\s\S]*\}/)
|
||||
assert.doesNotMatch(aiMode, /import \{ renderMarkdown \} from '\.\.\/\.\.\/utils\/markdown\.js'/)
|
||||
assert.match(aiMode, /buildStewardPlanRequest/)
|
||||
assert.match(aiMode, /buildStewardPlanMessageText/)
|
||||
assert.match(aiMode, /buildStewardSuggestedActions/)
|
||||
assert.match(aiMode, /const emit = defineEmits\(\['conversation-change', 'conversation-history-change', 'open-document'\]\)/)
|
||||
assert.match(aiMode, /function startInlineConversation\(prompt, entry = \{\}, files = \[\]\)/)
|
||||
assert.match(aiMode, /activateInlineConversation\(\{[\s\S]*title:[\s\S]*\}\)[\s\S]*conversationMessages\.value\.push\(createInlineMessage\('user'/)
|
||||
assert.match(aiMode, /persistCurrentConversation\(\)/)
|
||||
assert.match(aiMode, /refreshConversationHistory\(\)/)
|
||||
assert.match(aiMode, /fetchStewardPlanStream\(/)
|
||||
assert.match(aiMode, /fetchStewardPlan\(/)
|
||||
assert.match(aiMode, /const INLINE_ANSWER_STREAM_CHUNK_SIZE = 6/)
|
||||
assert.match(aiMode, /function updateInlineMessageContent\(message, content\)/)
|
||||
assert.match(aiMode, /async function streamInlineAssistantContent\(messageId, content\)/)
|
||||
assert.match(aiMode, /const requiredApplicationContinuationFlow = resolveRequiredApplicationGateContinuationFlow\(normalizedPlan\)/)
|
||||
assert.match(aiMode, /const finalMessageText = requiredApplicationContinuationFlow[\s\S]*buildAiRequiredApplicationGateAutoMessage\(normalizedPlan, requiredApplicationContinuationFlow\)[\s\S]*buildStewardPlanMessageText\(plan\)/)
|
||||
assert.match(aiMode, /const hasServerStreamedContent = Boolean\(String\(pendingMessage\.content \|\| ''\)\.trim\(\)\)/)
|
||||
assert.match(aiMode, /if \(!hasServerStreamedContent\) \{[\s\S]*await streamInlineAssistantContent\(pendingMessage\.id, finalMessageText\)[\s\S]*\}/)
|
||||
assert.match(aiMode, /if \(actionType === AI_APPLICATION_ACTION_SUBMIT\) \{[\s\S]*buildInlineApplicationResultTable\(draftPayload/)
|
||||
assert.match(aiMode, /需要查看完整详情时,请点击卡片“操作”行的“查看”进入单据详情。/)
|
||||
assert.doesNotMatch(aiMode, /\*\*申请单号:\*\*/)
|
||||
assert.doesNotMatch(aiMode, /createInlineMessage\('assistant', buildStewardPlanMessageText\(plan\)/)
|
||||
assert.doesNotMatch(aiMode, /runOrchestrator\(/)
|
||||
assert.doesNotMatch(aiMode, /buildFallbackAnswer/)
|
||||
assert.doesNotMatch(aiMode, /已使用本地回复/)
|
||||
assert.doesNotMatch(aiMode, /emit\('open-assistant'/)
|
||||
assert.match(aiModeSurface, /import \{ fetchSettings \} from '\.\.\/\.\.\/services\/settings\.js'/)
|
||||
assert.match(aiModeSurface, /fetchStewardPlan,[\s\S]*fetchStewardPlanStream[\s\S]*services\/steward\.js'/)
|
||||
assert.match(aiModeSurface, /import \{ useWorkbenchComposerDate \} from '\.\.\/useWorkbenchComposerDate\.js'/)
|
||||
assert.match(aiModeSurface, /loadAiWorkbenchConversationHistory/)
|
||||
assert.match(aiModeSurface, /saveAiWorkbenchConversation/)
|
||||
assert.match(aiModeSurface, /deleteAiWorkbenchConversation/)
|
||||
assert.match(aiModeSurface, /import \{ renderAiConversationHtml \} from '\.\.\/\.\.\/utils\/aiConversationHtmlRenderer\.js'/)
|
||||
assert.match(aiModeSurface, /function renderInlineConversationHtml\(content\) \{[\s\S]*return renderAiConversationHtml\(content\)[\s\S]*\}/)
|
||||
assert.doesNotMatch(aiModeSurface, /import \{ renderMarkdown \} from '\.\.\/\.\.\/utils\/markdown\.js'/)
|
||||
assert.match(aiModeSurface, /buildStewardPlanRequest/)
|
||||
assert.match(aiModeSurface, /buildStewardPlanMessageText/)
|
||||
assert.match(aiModeSurface, /buildStewardSuggestedActions/)
|
||||
assert.match(aiModeSurface, /const emit = defineEmits\(\['conversation-change', 'conversation-history-change', 'open-document'\]\)/)
|
||||
assert.match(aiModeSurface, /function startInlineConversation\(prompt, entry = \{\}, files = \[\]\)/)
|
||||
assert.match(aiModeSurface, /activateInlineConversation\(\{[\s\S]*title:[\s\S]*\}\)[\s\S]*conversationMessages\.value\.push\(createInlineMessage\('user'/)
|
||||
assert.match(aiModeSurface, /persistCurrentConversation\(\)/)
|
||||
assert.match(aiModeSurface, /refreshConversationHistory\(\)/)
|
||||
assert.match(aiModeSurface, /fetchStewardPlanStream\(/)
|
||||
assert.match(aiModeSurface, /fetchStewardPlan\(/)
|
||||
assert.match(aiModeSurface, /const INLINE_ANSWER_STREAM_CHUNK_SIZE = 6/)
|
||||
assert.match(aiModeSurface, /function updateInlineMessageContent\(message, content\)/)
|
||||
assert.match(aiModeSurface, /async function streamInlineAssistantContent\(messageId, content\)/)
|
||||
assert.match(aiModeSurface, /const requiredApplicationContinuationFlow = resolveRequiredApplicationGateContinuationFlow\(normalizedPlan\)/)
|
||||
assert.match(aiModeSurface, /const finalMessageText = requiredApplicationContinuationFlow[\s\S]*buildAiRequiredApplicationGateAutoMessage\(normalizedPlan, requiredApplicationContinuationFlow\)[\s\S]*buildStewardPlanMessageText\(plan\)/)
|
||||
assert.match(aiModeSurface, /const hasServerStreamedContent = Boolean\(String\(pendingMessage\.content \|\| ''\)\.trim\(\)\)/)
|
||||
assert.match(aiModeSurface, /if \(!hasServerStreamedContent\) \{[\s\S]*await streamInlineAssistantContent\(pendingMessage\.id, finalMessageText\)[\s\S]*\}/)
|
||||
assert.match(aiModeSurface, /if \(actionType === AI_APPLICATION_ACTION_SUBMIT\) \{[\s\S]*buildInlineApplicationResultTable\(draftPayload/)
|
||||
assert.match(aiModeSurface, /需要查看完整详情时,请点击卡片“操作”行的“查看”进入单据详情。/)
|
||||
assert.doesNotMatch(aiModeSurface, /\*\*申请单号:\*\*/)
|
||||
assert.doesNotMatch(aiModeSurface, /createInlineMessage\('assistant', buildStewardPlanMessageText\(plan\)/)
|
||||
assert.doesNotMatch(aiModeSurface, /runOrchestrator\(/)
|
||||
assert.doesNotMatch(aiModeSurface, /buildFallbackAnswer/)
|
||||
assert.doesNotMatch(aiModeSurface, /已使用本地回复/)
|
||||
assert.doesNotMatch(aiModeSurface, /emit\('open-assistant'/)
|
||||
assert.match(aiModeStyles, /--ai-theme-rgb:\s*var\(--theme-primary-rgb/)
|
||||
assert.match(aiModeStyles, /\.workbench-ai-mode\s*\{[\s\S]*min-height:\s*100%;[\s\S]*background:/)
|
||||
assert.match(aiModeStyles, /\.workbench-ai-mode\.has-conversation\s*\{[\s\S]*place-items:\s*stretch;[\s\S]*padding:\s*0;/)
|
||||
@@ -339,6 +386,11 @@ test('AI mode screen follows the approved reference structure', () => {
|
||||
assert.match(fileCardRule, /border-radius:\s*16px;/)
|
||||
assert.match(aiModeStyles, /\.workbench-ai-file-card__body strong,[\s\S]*\.workbench-ai-file-card__body small\s*\{[\s\S]*text-overflow:\s*ellipsis;/)
|
||||
assert.match(aiModeStyles, /\.workbench-ai-file-card__remove\s*\{[\s\S]*border-radius:\s*999px;/)
|
||||
assert.match(aiModeStyles, /\.ai-attachment-association-card/)
|
||||
assert.match(aiModeStyles, /\.ai-ocr-recognition-card/)
|
||||
assert.match(aiModeStyles, /\.ai-attachment-association__note/)
|
||||
assert.match(aiModeStyles, /\.workbench-ai-ocr-detail-panel/)
|
||||
assert.match(aiModeStyles, /\.workbench-ai-ocr-document__fields/)
|
||||
assert.match(composerRule, /min-height:\s*154px;/)
|
||||
assert.match(composerRule, /grid-template-rows:\s*minmax\(80px,\s*1fr\) auto;/)
|
||||
assert.match(composerTextareaRule, /min-height:\s*80px;/)
|
||||
@@ -365,23 +417,23 @@ test('AI mode screen follows the approved reference structure', () => {
|
||||
assert.match(aiModeStyles, /\.workbench-ai-send-btn\s*\{[\s\S]*animation:\s*workbenchAiControlIn/)
|
||||
assert.match(aiModeStyles, /\.workbench-ai-action:nth-child\(4\)\s*\{[\s\S]*animation-delay:\s*520ms;/)
|
||||
assert.match(aiModeStyles, /\.workbench-ai-conversation\s*\{[\s\S]*grid-template-rows:\s*minmax\(0,\s*1fr\) auto;/)
|
||||
assert.match(aiMode, /const inlineConversationAutoScrollPinned = ref\(true\)/)
|
||||
assert.match(aiMode, /const INLINE_AUTO_SCROLL_THRESHOLD = 96/)
|
||||
assert.match(aiMode, /const INLINE_LAYOUT_SETTLE_SCROLL_DELAY_MS = 260/)
|
||||
assert.match(aiMode, /function isInlineConversationNearBottom\(\)/)
|
||||
assert.match(aiMode, /function handleInlineConversationScroll\(\)\s*\{[\s\S]*inlineConversationAutoScrollPinned\.value = isInlineConversationNearBottom\(\)[\s\S]*\}/)
|
||||
assert.match(aiMode, /function forceInlineConversationToBottom\(\)/)
|
||||
assert.match(aiMode, /el\.scrollTop = el\.scrollHeight/)
|
||||
assert.match(aiMode, /function scrollInlineConversationToBottom\(options = \{\}\)/)
|
||||
assert.match(aiMode, /const shouldScroll = options\.force !== false/)
|
||||
assert.match(aiMode, /if \(!shouldScroll\) \{[\s\S]*return[\s\S]*\}/)
|
||||
assert.match(aiMode, /window\.requestAnimationFrame\(\(\) => \{[\s\S]*forceInlineConversationToBottom\(\)[\s\S]*\}\)/)
|
||||
assert.match(aiMode, /window\.setTimeout\(\(\) => \{[\s\S]*if \(inlineConversationAutoScrollPinned\.value\) \{[\s\S]*forceInlineConversationToBottom\(\)[\s\S]*\}[\s\S]*\}, INLINE_LAYOUT_SETTLE_SCROLL_DELAY_MS\)/)
|
||||
assert.match(aiMode, /const shouldAutoScroll = inlineConversationAutoScrollPinned\.value[\s\S]*updateInlineMessageContent\(message, streamedContent\)[\s\S]*scrollInlineConversationToBottom\(\{ force: shouldAutoScroll \}\)/)
|
||||
assert.match(aiMode, /const shouldAutoScroll = inlineConversationAutoScrollPinned\.value[\s\S]*appendInlineMessageContent\(message, data\.delta \|\| data\.content \|\| data\.text \|\| ''\)[\s\S]*scrollInlineConversationToBottom\(\{ force: shouldAutoScroll \}\)/)
|
||||
assert.match(aiMode, /inlineConversationAutoScrollPinned\.value = true[\s\S]*conversationMessages\.value\.push\(createInlineMessage\('user', cleanPrompt\)\)/)
|
||||
assert.match(aiMode, /function openInlineRecentConversation\(item = \{\}\) \{[\s\S]*inlineConversationAutoScrollPinned\.value = true[\s\S]*conversationMessages\.value =/)
|
||||
assert.doesNotMatch(aiMode, /scrollTo\(\{ top: el\.scrollHeight, behavior: 'smooth' \}\)/)
|
||||
assert.match(aiModeSurface, /const inlineConversationAutoScrollPinned = ref\(true\)/)
|
||||
assert.match(aiModeSurface, /const INLINE_AUTO_SCROLL_THRESHOLD = 96/)
|
||||
assert.match(aiModeSurface, /const INLINE_LAYOUT_SETTLE_SCROLL_DELAY_MS = 260/)
|
||||
assert.match(aiModeSurface, /function isInlineConversationNearBottom\(\)/)
|
||||
assert.match(aiModeSurface, /function handleInlineConversationScroll\(\)\s*\{[\s\S]*inlineConversationAutoScrollPinned\.value = isInlineConversationNearBottom\(\)[\s\S]*\}/)
|
||||
assert.match(aiModeSurface, /function forceInlineConversationToBottom\(\)/)
|
||||
assert.match(aiModeSurface, /el\.scrollTop = el\.scrollHeight/)
|
||||
assert.match(aiModeSurface, /function scrollInlineConversationToBottom\(options = \{\}\)/)
|
||||
assert.match(aiModeSurface, /const shouldScroll = options\.force !== false/)
|
||||
assert.match(aiModeSurface, /if \(!shouldScroll\) \{[\s\S]*return[\s\S]*\}/)
|
||||
assert.match(aiModeSurface, /window\.requestAnimationFrame\(\(\) => \{[\s\S]*forceInlineConversationToBottom\(\)[\s\S]*\}\)/)
|
||||
assert.match(aiModeSurface, /window\.setTimeout\(\(\) => \{[\s\S]*if \(inlineConversationAutoScrollPinned\.value\) \{[\s\S]*forceInlineConversationToBottom\(\)[\s\S]*\}[\s\S]*\}, INLINE_LAYOUT_SETTLE_SCROLL_DELAY_MS\)/)
|
||||
assert.match(aiModeSurface, /const shouldAutoScroll = inlineConversationAutoScrollPinned\.value[\s\S]*updateInlineMessageContent\(message, streamedContent\)[\s\S]*scrollInlineConversationToBottom\(\{ force: shouldAutoScroll \}\)/)
|
||||
assert.match(aiModeSurface, /const shouldAutoScroll = inlineConversationAutoScrollPinned\.value[\s\S]*appendInlineMessageContent\(message, data\.delta \|\| data\.content \|\| data\.text \|\| ''\)[\s\S]*scrollInlineConversationToBottom\(\{ force: shouldAutoScroll \}\)/)
|
||||
assert.match(aiModeSurface, /inlineConversationAutoScrollPinned\.value = true[\s\S]*conversationMessages\.value\.push\(createInlineMessage\('user', cleanPrompt\)\)/)
|
||||
assert.match(aiModeSurface, /function openInlineRecentConversation\(item = \{\}\) \{[\s\S]*inlineConversationAutoScrollPinned\.value = true[\s\S]*conversationMessages\.value =/)
|
||||
assert.doesNotMatch(aiModeSurface, /scrollTo\(\{ top: el\.scrollHeight, behavior: 'smooth' \}\)/)
|
||||
assert.match(aiModeStyles, /\.workbench-ai-thread\s*\{[\s\S]*display:\s*flex;[\s\S]*flex-direction:\s*column;[\s\S]*overflow-y:\s*auto;[\s\S]*scrollbar-width:\s*none;/)
|
||||
assert.match(aiModeStyles, /\.workbench-ai-thread\s*>\s*:first-child\s*\{[\s\S]*margin-top:\s*auto;/)
|
||||
assert.match(aiModeStyles, /\.workbench-ai-message\s*\{[\s\S]*flex:\s*0 0 auto;/)
|
||||
@@ -436,3 +488,15 @@ test('AI mode screen follows the approved reference structure', () => {
|
||||
assert.ok(pngPresentation.minimumForegroundWidthRatio > 0.9)
|
||||
assert.ok(pngPresentation.minimumForegroundHeightRatio > 0.9)
|
||||
})
|
||||
|
||||
test('AI mode normal assistant requests include OCR context for uploaded receipts', () => {
|
||||
assert.match(aiModeSurface, /function isLikelyAiModeOcrFile\(file = \{\}\)/)
|
||||
assert.match(aiModeSurface, /async function collectAiModeReceiptContext\(files = \[\]\)/)
|
||||
assert.match(aiModeSurface, /collectReceiptFiles\(\{[\s\S]*files:\s*ocrFiles,[\s\S]*recognizeOcrFiles[\s\S]*\}\)/)
|
||||
assert.match(aiModeSurface, /const receiptContext = await collectAiModeReceiptContext\(files\)/)
|
||||
assert.match(aiModeSurface, /ocr_summary:\s*receiptContext\.ocrSummary/)
|
||||
assert.match(aiModeSurface, /ocr_documents:\s*receiptContext\.ocrDocuments/)
|
||||
assert.match(aiModeSurface, /attachment_names:\s*receiptContext\.attachmentNames/)
|
||||
assert.match(aiModeSurface, /attachment_count:\s*receiptContext\.attachmentCount/)
|
||||
assert.match(aiModeSurface, /ocr_source_file_names:\s*receiptContext\.ocrSourceFileNames/)
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user