| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138 |
- import assert from 'node:assert/strict'
- import { readFileSync } from 'node:fs'
- import vm from 'node:vm'
- import { resolveDetailAnchor, detailAnchorLocation } from '../../../src/modules/cockpit/domain/detailAnchor.js'
- import { solvePointAnchor } from '../../../src/modules/cockpit/domain/pointAnchor.js'
- const root = new URL('../../../src/modules/cockpit/', import.meta.url)
- const read = (path) => readFileSync(new URL(path, root), 'utf8')
- const engine = read('services/cesiumEngine.js')
- const fn = (name) => {
- const match = engine.match(new RegExp(`(?:export )?function ${name}\\([^]*?\\n\\}`))
- assert.ok(match, `${name} exists`)
- return match[0].replace(/^export /, '')
- }
- const property = (value) => ({ getValue: () => value })
- const core = {
- id: 'device|MP-001|EQ-A', show: true,
- properties: { deviceCode: property('EQ-A') },
- billboard: { scale: 1, color: { alpha: 1 }, rotation: 0 },
- }
- core.billboard.pixelOffset = property({ x: 42, y: -26 })
- const halo = {
- id: 'fx|MP-001|EQ-A|ring0', show: true,
- properties: { effectFx: property(true), effectFxFor: property('EQ-A'), effectFxStatus: property('WARNING'), effectFxKind: property('ring'), effectFxLayer: property(0) },
- billboard: { scale: 1, color: { alpha: 1 }, rotation: 0 },
- }
- const source = { entities: { values: [core, halo] } }
- let renderRequests = 0
- const viewer = { scene: { requestRender() { renderRequests += 1 } } }
- const context = vm.createContext({
- deviceEffectState: { viewer, plan: { animated: [{ code: 'EQ-A', effect: 'blink' }] }, selectedCode: '', tick: 0 },
- deviceSources: new Map([[viewer, source]]),
- baseStyleOf: () => ({ scale: 0.75, alpha: 0.45 }),
- DEVICE_EFFECT: { SPIN: 'spin', BLINK: 'blink' },
- Color: { WHITE: { withAlpha: (alpha) => ({ alpha }) } },
- blinkStrengthAt: () => 0.25, pulseStrengthAt: () => 0,
- spinAngleAt: () => 1, glowStrengthAt: () => 0, glowScaleAt: () => 1,
- ringFrameAt: () => ({ rings: [{ scale: 2, alpha: 0.8 }] }), Date,
- Cartesian2: class { constructor(x, y) { this.x = x; this.y = y } },
- })
- vm.runInContext(`${fn('animatedByCode')}\n${fn('writeEffectFrame')}\nwriteEffectFrame()`, context)
- let failures = 0
- const check = (name, action) => {
- try { action(); console.log(`PASS ${name}`) }
- catch (error) { failures += 1; console.error(`FAIL ${name}: ${String(error.message).split('\n')[0]}`) }
- }
- check('center icon remains opaque at the dimmest blink phase', () => assert.equal(core.billboard.color.alpha, 1))
- check('center icon never shrinks below its normal size', () => assert.ok(core.billboard.scale >= 1))
- check('blink applies to the outer halo while center stays opaque', () => assert.equal(halo.billboard.color.alpha, 0.2))
- check('halo follows the expanded device pixelOffset', () => assert.equal(halo.billboard.pixelOffset, core.billboard.pixelOffset))
- check('active effects request a real render', () => assert.ok(renderRequests > 0))
- check('halo disappears with its device', () => {
- core.show = false
- vm.runInContext('writeEffectFrame()', context)
- assert.equal(halo.show, false)
- core.show = true
- })
- const objects = [{ kind: 'monitoring-point', code: 'MP-001', coordinate: { renderable: true } }]
- const devices = [
- { code: 'EQ-A', pointCode: 'MP-001', coordinate: { renderable: true } },
- { code: 'EQ-B', pointCode: 'MP-001', coordinate: { renderable: true } },
- { code: 'EQ-X', pointCode: 'MP-001', coordinate: { renderable: false, validated: true, reason: 'UNVERIFIED_COORDINATE' } },
- ]
- const target = (level, deviceCode = '') => resolveDetailAnchor({ selection: { level, pointCode: 'MP-001', deviceCode }, objects, devices })
- check('equipment A anchors to A while keeping point ownership', () => {
- assert.equal(target('equipment', 'EQ-A').code, 'EQ-A')
- assert.equal(target('equipment', 'EQ-A').pointCode, 'MP-001')
- assert.equal(target('equipment', 'EQ-A').kind, 'monitoring-device')
- })
- check('equipment A to B changes anchor identity', () => assert.equal(target('equipment', 'EQ-B').code, 'EQ-B'))
- check('back to point summary restores point anchor', () => assert.equal(target('point', 'EQ-B').code, 'MP-001'))
- check('no-location equipment uses its own verdict', () => assert.equal(detailAnchorLocation(target('equipment', 'EQ-X')).reasonText, '坐标未经校验'))
- check('missing equipment does not borrow its parent location', () => assert.equal(detailAnchorLocation(target('equipment', 'EQ-missing')).present, false))
- check('unassociated equipment anchors to itself', () => {
- const standalone = { kind: 'unassociated-equipment', code: 'EQ-U' }
- assert.equal(resolveDetailAnchor({ selection: { level: 'equipment', deviceCode: 'EQ-U' }, objects: [standalone] }).object, standalone)
- })
- check('closed detail has no anchor', () => assert.equal(target('none', 'EQ-A'), null))
- check('shell consumes anchor policy and includes line movement in its update key', () => {
- assert.ok(/resolveDetailAnchor\(/.test(read('CockpitShell.vue')))
- assert.ok(/JSON.stringify\(layout.line\)/.test(read('CockpitShell.vue')))
- })
- // Execute the production projection with a small Cesium public-API double.
- const entity = {
- isShowing: true, show: true, position: property({ longitude: 1, latitude: 2 }),
- properties: { deviceCode: property('EQ-A'), monitoringPointCode: property('MP-001') },
- billboard: { pixelOffset: property({ x: 42, y: -26 }), scale: property(1.6), width: property(14), height: property(14) },
- }
- const projectionViewer = {
- clock: { currentTime: 0 }, canvas: { getBoundingClientRect: () => ({ left: 12, top: 102 }) },
- scene: { globe: { ellipsoid: { cartesianToCartographic: (p) => p }, getHeight: () => 123 } },
- camera: { positionWC: {}, directionWC: {} },
- }
- function Cartesian3() {}
- Cartesian3.fromRadians = (longitude, latitude, height) => ({ longitude, latitude, height })
- Cartesian3.subtract = () => ({})
- Cartesian3.dot = () => 1
- const projectionContext = vm.createContext({
- Cartesian3,
- EllipsoidalOccluder: class { isPointVisible() { return true } },
- SceneTransforms: { worldToWindowCoordinates: (_scene, position) => { assert.equal(position.height, 123); return { x: 500, y: 320 } } },
- deviceSources: new Map([[projectionViewer, { show: true, entities: { values: [entity] } }]]),
- viewer: projectionViewer,
- })
- vm.runInContext(`${fn('findDeviceMarker')}\n${fn('projectMarkerEntity')}\n${fn('projectDeviceMarker')}`, projectionContext)
- check('device anchor includes terrain, fan pixelOffset and selected icon size', () => {
- const at = vm.runInContext("projectDeviceMarker(viewer, 'EQ-A', 'MP-001')", projectionContext)
- assert.equal(at.x, 554)
- assert.equal(at.y, 396)
- assert.equal(at.iconSize, 22.400000000000002)
- const layout = solvePointAnchor({ anchor: at, canvas: { left: 12, top: 102, right: 1650, bottom: 842 }, iconSize: at.iconSize })
- assert.ok(layout.rect.left > at.x + at.iconSize / 2)
- assert.equal(layout.line.from.x, at.x + at.iconSize / 2)
- })
- check('hidden fan device has no fake parent anchor', () => {
- entity.show = false
- assert.equal(vm.runInContext("projectDeviceMarker(viewer, 'EQ-A', 'MP-001')", projectionContext), null)
- })
- check('effects off cancels the loop without hiding the center icon', () => {
- const cancelled = []
- context.window = { cancelAnimationFrame: (id) => cancelled.push(id) }
- context.clearRingEntities = () => {}
- context.deviceEffectState.raf = 17
- vm.runInContext(`${fn('applyDeviceEffects')}\napplyDeviceEffects(deviceEffectState.viewer, { enabled: false, animated: [] })`, context)
- assert.deepEqual(cancelled, [17])
- assert.equal(context.deviceEffectState.raf, 0)
- assert.equal(core.billboard.color.alpha, 1)
- })
- check('wrong parent identity never resolves another device marker', () => {
- entity.show = true
- assert.equal(vm.runInContext("projectDeviceMarker(viewer, 'EQ-A', 'MP-other')", projectionContext), null)
- })
- check('pending device does not pretend to have a known location failure', () => {
- assert.equal(detailAnchorLocation({ code: 'EQ-pending', object: { placement: 'pending' } }).present, false)
- })
- console.log(`T47 readability/anchor: ${failures ? 'FAILED' : 'passed'}`)
- process.exitCode = failures ? 1 : 0
|