// 票 56(第二/三轮):所有面板顶部摘要统一折叠,**只有点击**展开/收起。 // 离线闸门:静态断言渲染层 + 用真实注册表做说明字段的**行为层双向完备性普查**。 import fs from 'node:fs' import assert from 'node:assert/strict' import { MONITORING_FAULT_GAP, monitoringSummaryPanel } from '../../../src/modules/cockpit/domain/overviewPanels.js' import { implementedPanelIds, panelView } from '../../../src/modules/cockpit/domain/panelViews.js' const ROOT = new URL('../../../', import.meta.url) const panelBlocks = fs.readFileSync(new URL('src/modules/cockpit/components/PanelBlocks.vue', ROOT), 'utf8') const monitoringPanels = fs.readFileSync(new URL('src/modules/cockpit/domain/monitoringPanels.js', ROOT), 'utf8') const overviewPanels = fs.readFileSync(new URL('src/modules/cockpit/domain/overviewPanels.js', ROOT), 'utf8') /** * 把每个已实现面板的视图真的算一遍,收集「视图级说明字段」(`*Note` / `note` / `gap`)。 * 走注册表而不是正则扫源码:正则会被换缩进/换行写法静默绕过(票 43 的"守卫比要守的宽"教训)。 */ function panelViewRegistryProbe() { const found = new Set() const built = [] for (const panelId of implementedPanelIds()) { let view = null try { view = panelView(panelId, {}, null, undefined) } catch { view = null } if (!view) continue built.push(panelId) for (const key of Object.keys(view)) { if (key === 'gap' || key === 'note' || /Note$/.test(key)) found.add(key) } } return { fields: [...found], built } } const registry = panelViewRegistryProbe() let n = 0 const ok = (cond, msg) => { assert.ok(cond, `A${++n} ${msg}`); console.log(`PASS A${n} ${msg}`) } // A 组:折叠入口只有点击一条路径 ok(/summaryOpen = ref\(false\)/.test(panelBlocks), '默认折叠(summaryOpen 初值 false)') ok(/@click="summaryOpen = !summaryOpen"/.test(panelBlocks), '点击是唯一 toggle 路径') ok(!/summaryHover|summaryFocus|summaryPinned|@mouseenter="summary|@focusin="summary/.test(panelBlocks), 'hover/focus 自动展开已撤销(第二轮维护者裁定)') ok(/:aria-expanded="summaryOpen"/.test(panelBlocks), 'aria-expanded 绑定同一个状态') ok(/:aria-controls="summaryDetailsId"/.test(panelBlocks) && /panel-summary-details-\$\{props\.view\.name/.test(panelBlocks), 'aria-controls 按面板名唯一(多面板共存不串 target)') ok(!/@keydown\.(enter|space)/.test(panelBlocks), '不写显式 keydown:原生 button 的 Enter/Space 已触发 click,重复绑定会双触发互相抵消') // B 组:摘要条目完备 —— 每条保留原 data-* 断言面 const EXPECTED = [ ['industryNote', 'data-panel-industry'], ['typeNote', 'data-panel-type-note'], ['eventNote', 'data-panel-event-note'], ['rangeNote', 'data-panel-range-note'], ['gap', 'data-panel-gap'], ['offlineNote', 'data-panel-offline-note'], ['pageNote', 'data-panel-page-note'], ['locateNote', 'data-locate-note'], ['excludedNote', 'data-panel-excluded-note'], ['linkNote', 'data-panel-link-note'], ['note', 'data-panel-note'], // 票 64:运行信息新增「数据来源与口径」说明。**这条是普查自己抓出来的**—— // 加进 domain 而没加进本表时 A18 当场判红(漏网即隐形),按成例补条目并保留断言面。 ['sourceNote', 'data-panel-source-note'], ] for (const [field, hook] of EXPECTED) { ok( panelBlocks.includes(`field: '${field}', hook: '${hook}'`), `摘要条目 ${field} 折叠并保留 ${hook}(走查脚本 t17/t18/t20 选择器不变)`, ) } // C 组:**行为层双向完备性普查**(t44 的机制,不靠人记得去查,也不靠正则匹缩进)。 // 用真实注册表算出全部 15 个面板的视图,收集「视图级说明字段」的并集: // · domain 新增了说明字段而不进折叠表 → 漏网判红(它会在界面上隐形); // · 折叠表里有字段而任何面板都不再声明 → 死接线判红(无消费点的接线早晚漂移)。 const declared = new Set(registry.fields) const tabled = new Set(EXPECTED.map(([field]) => field)) const missing = [...declared].filter((f) => !tabled.has(f)) ok(missing.length === 0, `domain 视图声明的说明字段全部进折叠表(漏网:${missing.join(', ') || '无'})`) const dead = [...tabled].filter((f) => !declared.has(f)) ok(dead.length === 0, `折叠表内字段仍有面板在声明(死接线:${dead.join(', ') || '无'})`) ok( ['gap', 'note', 'linkNote', 'excludedNote', 'offlineNote', 'pageNote'].every((f) => declared.has(f)), `普查真的见到这六类(当前并集 ${declared.size} 条,守卫面没被写窄)`, ) ok( registry.built.length >= 12, `普查覆盖到 12+ 个面板视图(实际 ${registry.built.length} 个:${registry.built.join(', ')})`, ) // D 组:用户点名的「故障口径」真的落到运行监测面板的摘要里(行为层,不是正则) { const view = monitoringSummaryPanel({}, null) ok(view.gap === MONITORING_FAULT_GAP, '运行监测视图仍由 domain 单源给出故障口径 gap') ok(view.gap.includes('故障'), '故障口径文案未被动过') ok(tabled.has('gap'), '故障口径经折叠表的 gap 条目进入摘要(第三轮诉求)') } // E 组:旧的独立渲染必须清零(否则折叠形同虚设:同一句话画两遍或仍在头部外裸奔) ok(!/v-if="view\.(gap|offlineNote|pageNote|rangeNote|industryNote|typeNote|eventNote|locateNote|note|linkNote|excludedNote|sourceNote)\b/.test(panelBlocks), '没有任何说明字段在折叠入口之外独立渲染') ok(/v-show="summaryOpen"/.test(panelBlocks), '折叠用 v-show:文本仍在 DOM(信息不丢,只改呈现)') ok(/