| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163 |
- /** Component-level SSR integration; not a browser/layout or GPU validation. */
- import assert from 'node:assert/strict'
- import { readFileSync } from 'node:fs'
- import { registerHooks } from 'node:module'
- import { parse, compileScript, compileTemplate } from '@vue/compiler-sfc'
- import { compile } from '@vue/compiler-ssr'
- import { createRequire } from 'node:module'
- import { createSSRApp, computed } from 'vue'
- import { renderToString } from '@vue/server-renderer'
- import { createPinia, setActivePinia } from 'pinia'
- const root = new URL('../../../src/modules/cockpit/', import.meta.url)
- const observation = Symbol.for('t41.location-observation')
- globalThis[observation] = []
- const hooks = registerHooks({
- resolve(specifier, context, nextResolve) {
- const resolved = nextResolve(specifier, context)
- if (resolved.url.endsWith('/cockpit/services/mapService.js')) return { ...resolved, url: `${resolved.url}?t41-observed` }
- return resolved
- },
- load(url, context, nextLoad) {
- const mutation = process.env.T41_MUTATION
- if (mutation === 'marker-gate' && url.endsWith('/domain/modeLayerPlan.js')) {
- const source = readFileSync(new URL(url), 'utf8')
- const previous = /return Boolean\(point && mode !== MODE_LAYER\.INFRASTRUCTURE &&\s*\(mode !== MODE_LAYER\.WARNING_RESPONSE \|\| isAbnormalPoint\(point, abnormalCodes\)\)\)/
- assert.ok(previous.test(source), 'mutation matches current gate')
- return { format: 'module', shortCircuit: true, source: source.replace(previous,
- `return markers.some((item) => item.code === current.pointCode)`) }
- }
- if (mutation === 'drop-empty' && url.endsWith('/services/detailApi.js')) {
- const source = readFileSync(new URL(url), 'utf8')
- assert.ok(source.includes('toData: toMonitoringPointDetail,'), 'mutation matches point adapter')
- return { format: 'module', shortCircuit: true, source: source.replace('toData: toMonitoringPointDetail,',
- `toData: toMonitoringPointDetail, isEmpty: isEmptyBy((payload) => payload?.pointStatus === 'EMPTY'),`) }
- }
- if (url.endsWith('?t41-observed')) {
- const actual = url.replace('?t41-observed', '?t41-actual')
- return { format: 'module', shortCircuit: true, source: `
- import * as actual from ${JSON.stringify(actual)};
- export * from ${JSON.stringify(actual)};
- export function locateMonitoringPoint(...args) {
- globalThis[Symbol.for('t41.location-observation')].push(args);
- return actual.locateMonitoringPoint(...args);
- }
- ` }
- }
- if (url.endsWith('.json')) return { format: 'module', shortCircuit: true, source: `export default ${readFileSync(new URL(url), 'utf8')}` }
- if (url.endsWith('.vue')) {
- const { descriptor } = parse(readFileSync(new URL(url), 'utf8'), { filename: new URL(url).pathname })
- const script = compileScript(descriptor, { id: 't41-render', genDefaultAs: '__sfc__' })
- const template = compileTemplate({ source: descriptor.template.content, filename: new URL(url).pathname,
- id: 't41-render', ssr: true, ssrCssVars: [], compilerOptions: { bindingMetadata: script.bindings } })
- assert.deepEqual(template.errors, [])
- return { format: 'module', shortCircuit: true,
- source: `${script.content}\n${template.code}\n__sfc__.ssrRender = ssrRender; export default __sfc__;` }
- }
- return nextLoad(url, context)
- },
- })
- const { useDetailStore } = await import(new URL('stores/detailStore.js', root))
- const { useMapStore } = await import(new URL('stores/mapStore.js', root))
- const { useMonitoringStore } = await import(new URL('stores/monitoringStore.js', root))
- const { registerMockTransport, resetMockTransportsForTest } = await import(new URL('services/cockpitApi.js', root))
- const { planModeLayers } = await import(new URL('domain/modeLayerPlan.js', root))
- const { resolveDetailAnchor, detailAnchorLocation } = await import(new URL('domain/detailAnchor.js', root))
- const { default: PointDetailCard } = await import(new URL('components/PointDetailCard.vue', root))
- const pinia = createPinia(); setActivePinia(pinia)
- const config = { dataSource: 'mock', api: { baseUrl: '', token: '' } }
- const detail = useDetailStore(); detail.configure({ config })
- const map = useMapStore(); map.setup({ config })
- const ledger = useMonitoringStore()
- resetMockTransportsForTest()
- const code = 'P-FIXED'
- const validation = { state: 'INVALID', reason: 'OUTSIDE_PRIMARY_AREA', renderable: false, crs: 'WGS84' }
- registerMockTransport('/map-snapshot', () => ({ page: 1, pageSize: 2, total: 1, complete: true,
- snapshotAt: '2026-10-12T10:00:00+08:00', monitoringPoints: [{ pointCode: code, pointName: '区外监测点',
- pointStatus: 'NORMAL', deviceCodes: ['EQ-FIXED'], longitude: 110.1, latitude: 28.4, coordinateValidation: validation }], unassociatedEquipment: [] }))
- registerMockTransport(`/monitoring-points/${code}`, () => ({ monitoringPointCode: code, monitoringPointName: '区外监测点',
- pointStatus: 'NORMAL', statusLabel: '正常', devices: [{ deviceCode: 'EQ-FIXED', deviceName: '区外设备', deviceStatus: 'NORMAL', coordinateValidation: validation }],
- deviceStatistics: { totalDeviceCount: 1, normalDeviceCount: 1 }, coordinateValidation: validation }))
- await map.advance()
- await ledger.openLedgerPoint(code)
- // Use the production floating detail template without invoking shell lifecycle/server actions.
- const shell = readFileSync(new URL('CockpitShell.vue', root), 'utf8')
- const begin = shell.indexOf('<div v-if="detailOpen && !introVisible && selectionVisibleInMode && anchorLayout.visible')
- const end = shell.indexOf('</aside>', begin) + '</aside>'.length
- assert.ok(begin >= 0 && end > begin, 'production float template found')
- const ssrRender = new Function('require', compile(shell.slice(begin, end), { mode: 'function' }).code)(createRequire(import.meta.url))
- let mode = 'overview'
- let failures = 0
- let checks = 0
- async function check(name, action) {
- checks++
- try { await action(); console.log(`PASS ${name}`) }
- catch (error) { failures++; console.error(`FAIL ${name}: ${error.message.split('\n')[0]}`) }
- }
- const verdict = () => detailAnchorLocation(resolveDetailAnchor({ selection: detail.selection, objects: map.summary.objects }))
- async function render() {
- const plan = planModeLayers({ mode, entered: true, objects: map.summary.objects, selection: detail.selection })
- const reason = verdict().reasonText
- const app = createSSRApp({ components: { PointDetailCard }, ssrRender, setup() {
- return { detailOpen: detail.selection.level !== 'none', introVisible: false, selectionVisibleInMode: plan.selection.visible,
- anchorLayout: { visible: false, side: '', reason: 'marker-missing' }, fixedLocationReason: reason,
- selectedDetailCode: detail.selection.pointCode, anchorLeadStyle: {}, detailStyle: computed(() => reason ? {} : { visibility: 'hidden' }) }
- } })
- app.use(pinia)
- return renderToString(app)
- }
- await check('ledger public entry opens loaded blocked point with its own identity', () => {
- assert.equal(detail.selection.level, 'point'); assert.equal(detail.selection.pointCode, code)
- assert.equal(detail.sections.pointDetail.data.name, '区外监测点')
- })
- await check('overview mounts fixed detail and prints its precise no-location reason', async () => {
- const html = await render()
- assert.match(html, /data-anchor-state="fixed"/)
- assert.match(html, /data-point-no-location/)
- assert.ok(html.includes('不在主场景区范围内'))
- assert.ok(!html.includes('data-anchor-lead'))
- assert.ok(!html.includes('visibility:hidden'))
- })
- await check('blocked selection issues no location request', () => assert.equal(globalThis[observation].length, 0))
- await check('monitoring mode retains the fixed detail without fake map marker', async () => {
- mode = 'monitoring'
- const html = await render()
- assert.match(html, /data-anchor-state="fixed"/)
- assert.equal(planModeLayers({ mode, objects: map.summary.objects }).markers.length, 0)
- })
- await check('infrastructure suspends fixed detail but keeps selected identity', async () => {
- mode = 'infrastructure'
- assert.ok(!(await render()).includes('data-point-detail'))
- assert.equal(detail.selection.pointCode, code)
- })
- await check('switching back to overview restores fixed detail', async () => {
- mode = 'overview'; assert.match(await render(), /data-anchor-state="fixed"/)
- })
- await check('unloaded object is not falsely labelled no-location', () => {
- assert.equal(detailAnchorLocation(resolveDetailAnchor({ selection: { level: 'point', pointCode: 'P-LATER' }, objects: map.summary.objects })).present, false)
- })
- await check('no-device blocked point retains its name and EMPTY business status', async () => {
- detail.closeDetail()
- registerMockTransport(`/monitoring-points/${code}`, () => ({ monitoringPointCode: code, monitoringPointName: '区外监测点',
- pointStatus: 'EMPTY', statusLabel: '无设备', devices: [], deviceStatistics: { totalDeviceCount: 0 }, coordinateValidation: validation }))
- await ledger.openLedgerPoint(code)
- assert.equal(detail.sections.pointDetail.data?.name, '区外监测点')
- assert.equal(detail.sections.pointDetail.data?.status, 'EMPTY')
- assert.equal(detail.sections.pointDetail.data.statistics.total, 0)
- assert.equal(detail.sections.pointDetail.data.coordinate.renderable, false)
- assert.equal(detail.sections.pointDetail.data.coordinate.reason, 'OUTSIDE_PRIMARY_AREA')
- const html = await render()
- assert.ok(html.includes('区外监测点'))
- assert.ok(html.includes('无设备'))
- assert.match(html, /data-point-code-value[^>]*>P-FIXED<\/dd>/)
- assert.match(html, /data-point-device-count[^>]*>0<\/b>/)
- assert.equal(globalThis[observation].length, 0)
- })
- await check('closing removes detail without issuing a point-location request', async () => {
- detail.closeDetail(); assert.ok(!(await render()).includes('data-point-detail'))
- assert.equal(globalThis[observation].length, 0)
- })
- resetMockTransportsForTest(); delete globalThis[observation]; hooks.deregister()
- console.log(`T41 fixed-detail SSR: ${checks-failures}/${checks}, not browser verification`)
- process.exitCode = failures ? 1 : 0
|