import assert from 'node:assert/strict' import { readFileSync } from 'node:fs' import { parse } from '@vue/compiler-sfc' import { baseParse } from '@vue/compiler-dom' // Focused static/logic checks for the toolbar request; no server or browser. const source = readFileSync(new URL('../../../src/modules/cockpit/CockpitShell.vue', import.meta.url), 'utf8') const { descriptor, errors } = parse(source) assert.deepEqual(errors, [], 'Vue SFC must parse') const ast = baseParse(descriptor.template.content) const elements = [] function visit(node, parent = null) { if (node.type === 1) elements.push({ node, parent }) for (const child of node.children || []) visit(child, node.type === 1 ? node : parent) } visit(ast) const has = (node, name) => node.props.some((p) => (p.type === 6 && p.name === name) || (p.type === 7 && p.name === 'bind' && p.arg?.content === name)) const cls = (node, name) => node.props.some((p) => p.type === 6 && p.name === 'class' && p.value.content.split(/\s+/).includes(name)) const find = (predicate) => elements.find(({ node }) => predicate(node))?.node function within(node, ancestor) { while (node) { if (node === ancestor) return true node = elements.find((entry) => entry.node === node)?.parent } return false } const scope = find((n) => cls(n, 'cockpit-shell__scope')) const summary = find((n) => cls(n, 'cockpit-shell__status-summary')) const details = find((n) => has(n, 'data-status-details')) const refresh = find((n) => has(n, 'data-map-refresh')) assert.equal(elements.filter(({ node }) => has(node, 'data-status-toggle')).length, 1) assert.ok(within(details, summary), 'details belong to summary') for (const marker of ['data-map-summary-phase', 'data-section-board', 'data-ws-state', 'data-freshness']) { assert.ok(within(find((n) => has(n, marker)), details), `${marker} belongs to details`) } assert.equal(elements.filter(({ node }) => has(node, 'data-map-refresh')).length, 1) assert.ok(!within(refresh, details), 'refresh is outside details') assert.equal(scope.children.filter((n) => n.type === 1).at(-1), refresh, 'refresh is final toolbar control') assert.ok(!descriptor.template.content.includes('区内限位')) assert.ok(!descriptor.template.content.includes('{{ fogText }}')) assert.ok(!descriptor.template.content.includes('推送 · {{ runtimeStore.statusText }}')) // Execute the actual computed getters against controlled state (not token-presence checks). function getter(name) { const body = source.match(new RegExp(`const ${name} = computed\\(\\(\\) => \\{([\\s\\S]*?)\\n\\}\\)`))?.[1] assert.ok(body, `${name} getter exists`) return new Function('runtimeStore', 'overallFreshnessState', 'overallFreshnessLabel', 'mapSummaryView', body) } const text = getter('statusSummaryText') const tone = getter('statusSummaryTone') function checkState(view, phase, overall, expected, expectedTone) { const runtime = { freshness: { view, text: `fresh:${view}`, lastSuccessClock: '12:00:00' }, status: { state: 'open' }, statusText: 'ws:open' } const args = [runtime, { value: overall }, { value: 'partial warning' }, { value: { phase, label: `map:${phase}` } }] assert.equal(text(...args), expected) assert.equal(tone(...args), expectedTone) } checkState('current', 'complete', 'partial', 'partial warning', 'is-warning') for (const view of ['empty', 'stale', 'unavailable']) checkState(view, 'complete', 'current', `fresh:${view}`, 'is-warning') for (const phase of ['idle', 'loading', 'empty', 'failed', 'expired']) checkState('current', phase, 'current', `map:${phase}`, 'is-warning') checkState('current', 'complete', 'current', '更新 12:00:00', 'is-ok') const style = descriptor.styles.map((s) => s.content).join('\n') const rule = (selector) => style.slice(style.indexOf(`${selector} {`)).split('}')[0] assert.match(rule('.cockpit-shell__scope-left'), /overflow-x:\s*auto/) assert.match(rule('.cockpit-shell__status-details'), /max-height:/) assert.match(rule('.cockpit-shell__status-details'), /overflow:\s*auto/) assert.match(rule('.cockpit-shell__scope'), /overflow:\s*visible/) assert.match(rule('.cockpit-shell__scope'), /z-index:\s*3/) console.log('PASS toolbar AST nesting, rightmost refresh, removed diagnostics, summary state cases, and overflow guards')