import assert from 'node:assert/strict' import { readFileSync } from 'node:fs' import vm from 'node:vm' import * as Cesium from 'cesium' import { enhancementSelection, PIPE_ENHANCEMENT_KIND, planPipeEnhancement } from '../../../src/modules/cockpit/domain/pipeEnhancement.js' import { mountPipeEnhancement } from '../../../src/modules/cockpit/services/threePipeEnhancement.js' import { createPinia, setActivePinia } from 'pinia' import { usePipeEnhancementStore } from '../../../src/modules/cockpit/stores/pipeEnhancementStore.js' setActivePinia(createPinia()) const enhancementStore = usePipeEnhancementStore() const pipe = { code: 'P-001', name: '城南燃气管段', industry: 'GAS', diameter: 160, material: '钢', positions: [[110.1, 28.4], [110.101, 28.401]] } const component = { code: 'V-001', name: '城南阀门', industry: 'GAS', kind: 'valve', position: [110.1, 28.4] } const base = { visiblePipes: [pipe], visibleComponents: [component] } let checks = 0 function check(name, fn) { checks += 1; fn(); console.log(`PASS ${name}`) } check('Pinia store 持有并清理局部增强选择', () => { enhancementStore.select(PIPE_ENHANCEMENT_KIND.PIPE, 'P-001') assert.equal(enhancementStore.selected.kind, PIPE_ENHANCEMENT_KIND.PIPE) assert.equal(enhancementStore.selected.code, 'P-001') enhancementStore.clear() assert.equal(enhancementStore.selected, null) }) check('没有选中对象时不创建增强计划', () => { assert.equal(planPipeEnhancement(base).visible, false) assert.equal(planPipeEnhancement(base).reason, 'no-selection') }) check('局部计划只保留当前被选中的管段', () => { const plan = planPipeEnhancement({ ...base, selected: enhancementSelection(PIPE_ENHANCEMENT_KIND.PIPE, 'P-001') }) assert.equal(plan.visible, true) assert.equal(plan.object.code, 'P-001') assert.equal(plan.object.path.length, 2) assert.match(plan.disclaimer, /不代表工程竣工模型/) }) check('筛选移除对象时增强视图自动不可见', () => { const plan = planPipeEnhancement({ selected: enhancementSelection('pipe', 'P-001'), visiblePipes: [] }) assert.equal(plan.visible, false) assert.equal(plan.reason, 'object-not-visible') }) check('构件计划保留构件类型但不伪造管径', () => { const plan = planPipeEnhancement({ ...base, selected: enhancementSelection(PIPE_ENHANCEMENT_KIND.COMPONENT, 'V-001') }) assert.equal(plan.visible, true) assert.equal(plan.object.renderKind, 'valve') assert.equal(plan.object.diameter, null) }) check('未知选择类型被拒绝而不是默认成管段', () => { const plan = planPipeEnhancement({ ...base, selected: { kind: 'foreign', code: 'P-001' } }) assert.equal(plan.visible, false) assert.equal(plan.reason, 'invalid-selection') }) class Canvas { constructor() { this.listeners = new Map(); this.parentNode = null } addEventListener(name, fn) { this.listeners.set(name, fn) } removeEventListener(name) { this.listeners.delete(name) } } class Host { constructor() { this.clientWidth = 320; this.clientHeight = 220; this.children = []; this.ownerDocument = { createElement: () => new Canvas() } } appendChild(node) { node.parentNode = this; this.children.push(node) } removeChild(node) { this.children = this.children.filter((item) => item !== node); node.parentNode = null } } function rendererFactory({ canvas }) { const domElement = canvas || new Canvas() return { domElement, setPixelRatio() {}, setSize() {}, render() {}, dispose() {} } } check('渲染服务支持相同编码但不同对象类型的替换', () => { const host = new Host() const first = planPipeEnhancement({ ...base, selected: enhancementSelection('pipe', 'P-001') }) const viewer = mountPipeEnhancement(host, first, { rendererFactory }) const sameCodeComponent = { ...component, code: 'P-001' } const second = planPipeEnhancement({ visiblePipes: [pipe], visibleComponents: [sameCodeComponent], selected: enhancementSelection('component', 'P-001') }) viewer.sync(second) assert.equal(viewer.getState().title, '地下管线详情视图 · 构件') assert.equal(viewer.getState().code, 'P-001') viewer.destroy() assert.equal(viewer.getState().status, 'destroyed') }) const engineSource = readFileSync(new URL('../../../src/modules/cockpit/services/cesiumEngine.js', import.meta.url), 'utf8') const engineCode = engineSource .replace(/^import\s+[^]*?from\s+['"][^'"\n]+['"][ \t]*$/gm, '') .replace(/^import\s+['"][^'"\n]+['"][ \t]*$/gm, '') .replace(/^export /gm, '') const engineContext = vm.createContext({ ...Cesium, CesiumMath: Cesium.Math }) vm.runInContext(`${engineCode}\nglobalThis.pipePicker = pickPipeObjectAt;`, engineContext) const pipeSource = new Cesium.CustomDataSource('cockpit-pipelines') const entity = pipeSource.entities.add({ id: 'pipe:P-001', properties: { kind: 'pipe', code: 'P-001' } }) let picked = { id: entity } const pickViewer = { dataSources: { getByName: (name) => name === 'cockpit-pipelines' ? [pipeSource] : [] }, canvas: { getBoundingClientRect: () => ({ left: 20, top: 30 }) }, scene: { pick: () => picked }, } check('Cesium entity pick returns the selected pipeline identity', () => { const hit = engineContext.pipePicker(pickViewer, 120, 230) assert.equal(hit.kind, 'pipeline') assert.equal(hit.code, 'P-001') assert.equal(hit.objectKind, 'pipe') }) check('GPU instance pick resolves instanceId instead of batch first item', () => { picked = { id: { kind: 'pipe-model-batch', instances: ['pipe:P-001', 'component:V-001'] }, instanceId: 1 } const hit = engineContext.pipePicker(pickViewer, 120, 230) assert.equal(hit.kind, 'pipeline') assert.equal(hit.code, 'V-001') assert.equal(hit.objectKind, 'component') picked = { id: { kind: 'pipe-model-batch', instances: ['pipe:P-001'] }, instanceId: 7 } assert.equal(engineContext.pipePicker(pickViewer, 120, 230), null) }) console.log(`T23 Three.js enhancement: ${checks}/${checks}`)