import assert from "node:assert/strict"; import { readFileSync } from "node:fs"; import { dirname, resolve } from "node:path"; import { pathToFileURL } from "node:url"; import { fileURLToPath } from "node:url"; const scriptDir = dirname(fileURLToPath(import.meta.url)); const componentPath = resolve( scriptDir, "../src/pages/ChatMain/ChatLongAnswer/ParsedValueView.vue" ); const detailPagePath = resolve( scriptDir, "../src/pages/ChatMain/ChatLongAnswer/index.vue" ); const longTextCardPath = resolve(scriptDir, "../src/utils/longTextCard.js"); const componentSource = readFileSync(componentPath, "utf8"); const detailPageSource = readFileSync(detailPagePath, "utf8"); const longTextCardSource = readFileSync(longTextCardPath, "utf8"); const { appendLongTextChunk, createLongTextData, getLongTextSections, } = await import(pathToFileURL(longTextCardPath)); assert.doesNotMatch( longTextCardSource, /normalizeLongText/, "longTextCard should not contain field-specific normalize helpers" ); assert.doesNotMatch( longTextCardSource, /spot_longitude|spot_latitude|spot_tag/, "longTextCard should not know spot_locate child field names" ); assert.doesNotMatch( componentSource, /isGenericObjectSectionField/, "ParsedValueView should use renderFieldEntries instead of a separate generic object branch" ); assert.match( componentSource, /getRenderEntriesForObject/, "ParsedValueView should derive object rows through the existing entry renderer" ); assert.doesNotMatch( componentSource, /STRUCTURED_SECTION_CONFIG/, "ParsedValueView should not require per-field structured section config" ); assert.doesNotMatch( componentSource, /LONG_TEXT_KEYS\.(preparationSection|sectionSuggestion|pitfallSection|viewSection|suggestionSection|lightReminder|photoSpotSection|phoneSection|decisionSection|routeWarning|tourRoutes|facilitiesAlongTheWay)/, "ParsedValueView should not reference ordinary section keys individually" ); assert.doesNotMatch( componentSource, /content-body-section-dot|content-body-section-list/, "generic object arrays should keep the original list-card rendering style" ); assert.doesNotMatch( componentSource, /entry\.type\s*===\s*'(question-suggest|photo-list|aigc-componet|commodity-list|spot-locate)'/, "renderFieldEntries should only render generic text/image/list entries" ); assert.doesNotMatch( componentSource, /createSpecialFieldEntry/, "dedicated long text components should be selected by top-level computed branches only" ); assert.match( componentSource, /shouldRenderQuestionSuggest/, "top-level question_suggest fields should render through the dedicated chips" ); assert.match( componentSource, /shouldRenderCommodityList/, "top-level commodity_list fields should render through the dedicated product list" ); assert.match( componentSource, /shouldRenderPhotoList/, "top-level photo_list fields should render through the dedicated photo swiper" ); assert.match( componentSource, /shouldRenderAigcComponet/, "top-level aigc_componet fields should render through the dedicated AIGC card" ); assert.match( componentSource, /shouldRenderSpotLocate/, "top-level spot_locate fields should render through the dedicated POI card" ); assert.doesNotMatch( componentSource, /createRenderEntry\(key,\s*entryKey,\s*value\[key\]\)/, "nested object keys should not trigger dedicated long text components" ); assert.match( componentSource, /openMap\(spotLocateValue\)/, "ParsedValueView should render spot_locate with the dedicated POI action card" ); assert.match( componentSource, /getSpotLocateValue/, "ParsedValueView should own spot_locate value shaping" ); assert.doesNotMatch( componentSource, /spot_longitude:|spot_latitude:|spot_tag:/, "ParsedValueView should not read malformed spot_locate keys" ); assert.match( componentSource, /shouldRenderQuestionSuggest/, "ParsedValueView should render question_suggest with dedicated FAQ chips" ); assert.match( componentSource, /getQuestionSuggestItems/, "ParsedValueView should own question_suggest value shaping" ); assert.match( componentSource, /sendReply\(question\)/, "question_suggest chips should send the selected follow-up question" ); assert.doesNotMatch( detailPageSource, /LONG_TEXT_KEYS\.content\b/, "long answer detail page should not reference removed content key" ); assert.match( componentSource, /LONG_TEXT_KEYS\.contentImage\b/, "ParsedValueView should render content_image with the dedicated image style" ); const ignoredFieldKeysMatch = componentSource.match( /const\s+IGNORED_FIELD_KEYS\s*=\s*\[([\s\S]*?)\];/ ); assert.ok( ignoredFieldKeysMatch, "ParsedValueView should define IGNORED_FIELD_KEYS" ); assert.doesNotMatch( ignoredFieldKeysMatch[1], /["']content_summary["']/, "ParsedValueView should not ignore content_summary" ); const receivedKeys = [ "tag", "title", "content_summary", "scene_image", "content_image", "view_section", "suggestion_section", "light_reminder", "spot_locate", "question_suggest", ]; const longTextData = createLongTextData(); receivedKeys.forEach((key) => { appendLongTextChunk(longTextData, { contentKey: key, contentValue: key }); }); appendLongTextChunk(longTextData, { contentKey: "view_section_items", contentValue: "old flat field", }); assert.deepEqual( getLongTextSections(longTextData).map((section) => section.contentKey), [...receivedKeys, "view_section_items"], "long text detail sections should keep configured fields first and preserve unknown contentKey fields" );