| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139 |
- import test from 'node:test'
- import assert from 'node:assert/strict'
- import { readFile, access } from 'node:fs/promises'
- import { alarmCurveChoices, auditStatusTone, monitorStatusTone, groupAlarmDevices, findAlarmMenu, seriesPoints, rangeParams } from '../src/views/subSystem/alarm/alarmOperationsModel.js'
- test('detail status colors distinguish monitoring and audit states without treating missing values as zero',()=>{
- assert.equal(monitorStatusTone(0),'danger')
- assert.equal(monitorStatusTone('1'),'success')
- assert.equal(auditStatusTone(0),'warning')
- assert.equal(auditStatusTone('1'),'primary')
- assert.equal(auditStatusTone(2),'info')
- for(const status of [null,undefined,'',9]){
- assert.equal(monitorStatusTone(status),'info')
- assert.equal(auditStatusTone(status),'info')
- }
- })
- test('detail uses lightweight inline state labels and small tags, without old gray status cards',async()=>{
- const source=await readFile('src/views/subSystem/alarm/AlarmDetail.vue','utf8')
- for(const label of ['监测状态','审核结果','处置状态']) assert.ok(source.includes('<dt>'+label+'</dt>'))
- assert.equal((source.match(/size="small" effect="light"/g)||[]).length,3)
- assert.ok(source.includes('font-weight: 400'))
- assert.ok(source.includes('orderStatusType(alarm.orderStatus)'))
- assert.ok(!source.includes('<label>'))
- const styles=await readFile('src/views/subSystem/alarm/alarmOperations.scss','utf8')
- assert.ok(!styles.includes('.state-grid'))
- })
- test('curve choices exclude absent values, retain zero and deduplicate events without changing source rows',()=>{
- const rows=[{id:'null',actualValue:null},{id:'missing'},{id:'empty',actualValue:''},
- {id:'zero',actualValue:0},{id:'negative',actualValue:-1},{id:'text-zero',actualValue:'0'},
- {id:'zero',actualValue:0,warningType:'流量'}]
- const choices=alarmCurveChoices(rows)
- assert.deepEqual(choices.map(row=>row.id),['zero','negative','text-zero'])
- assert.equal(choices[0].warningType,'流量')
- assert.equal(rows.length,7)
- assert.deepEqual(alarmCurveChoices([]),[])
- })
- test('reminders request only unhandled alarms and no longer link to ledger',async()=>{
- const source=await readFile('src/views/subSystem/alarm/remind.vue','utf8')
- assert.match(source,/getAlarmReminders\(\{ \.\.\.scope.value, alarmStatus: 0/)
- assert.ok(!source.includes('报警清单'))
- assert.ok(!source.includes("navigate('alarmList'"))
- assert.ok(source.includes('事件详情'))
- })
- test('ledger labels are updated and the audit filter is removed without removing its table column',async()=>{
- const source=await readFile('src/views/subSystem/alarm/list.vue','utf8')
- assert.ok(source.includes('placeholder="请输入设备编码/名称"'))
- assert.ok(source.includes('<el-form-item label="报警时间">'))
- assert.ok(!source.includes('<el-form-item label="审核结果">'))
- assert.ok(!source.includes('filters.auditStatus'))
- assert.ok(!source.includes('auditStatus:'))
- assert.ok(source.includes('prop="auditStatusName" label="审核结果"'))
- })
- test('GIS filters curve options and entries without filtering map or alarm history',async()=>{
- const source=await readFile('src/views/subSystem/alarm/drainage/alarmGIS.vue','utf8')
- assert.ok(source.includes('alarmCurveChoices([...(selected.value?.alarms || []), ...history.value])'))
- assert.ok(source.includes('device.alarms.find(hasActualValue)'))
- assert.ok(source.includes('v-if="hasActualValue(row)"'))
- assert.ok(source.includes('watch(curveChoices'))
- assert.ok(source.includes("tab === 'curve' && currentCurve"))
- assert.ok(source.includes('暂无有实际值的报警指标'))
- assert.ok(!source.includes('actualValueOnly: true'))
- })
- test('analysis filters on server before pagination and rejects empty-value deep links',async()=>{
- const source=await readFile('src/views/subSystem/alarm/drainage/analysis.vue','utf8')
- assert.ok(source.includes('actualValueOnly: true'))
- assert.ok(source.includes('alarmCurveChoices(result.data.records || [])'))
- assert.ok(source.includes('if (!hasActualValue(result.data))'))
- assert.ok(source.indexOf('if (!hasActualValue(result.data))') < source.indexOf('choices.value.unshift(result.data)'))
- const detail=await readFile('src/views/subSystem/alarm/AlarmDetail.vue','utf8')
- assert.ok(detail.includes("alarm && hasActualValue(alarm) && menu('drainageAlarmAnalysis')"))
- })
- test('GIS groups events by device and uses highest current grade, ignoring recovered severity',()=>{
- const [device] = groupAlarmDevices([
- {id:'a',equipmentId:'E1',deviceCode:'D1',createTime:'2026-01-01',alarmLevel:1,alarmStatus:1,longitude:'110',latitude:'28'},
- {id:'b',equipmentId:'E1',deviceCode:'D1',createTime:'2026-01-02',alarmLevel:3,alarmStatus:0,longitude:'110',latitude:'28'},
- {id:'c',equipmentId:'E1',deviceCode:'D1',createTime:'2026-01-03',alarmLevel:2,alarmStatus:0,longitude:'110',latitude:'28'}
- ])
- assert.equal(device.alarms.length,3); assert.equal(device.alarmLevel,2); assert.equal(device.alarms[0].id,'c'); assert.equal(device.hasCoordinates,true)
- })
- test('GIS keeps missing or invalid coordinates in device list but does not map them',()=>{
- for(const [longitude,latitude] of [[null,28],['',28],[undefined,28],['nan',28],[181,28],[110,91],[0,0]]){
- const [device]=groupAlarmDevices([{deviceCode:'D1',longitude,latitude}]); assert.equal(device.hasCoordinates,false)
- }
- assert.equal(groupAlarmDevices([{deviceCode:'D1',longitude:0,latitude:28}])[0].hasCoordinates,true)
- })
- test('same leaf route selects only the matching authorized subsystem',()=>{
- const routes=[{path:'/water',children:[{path:'alarmList',query:'{"rootTypeId":1}'}]},{path:'/drainage',children:[{path:'alarmList',query:'{"rootTypeId":2}'}]}]
- assert.equal(findAlarmMenu(routes,'alarmList',2).path,'/drainage/alarmList')
- assert.equal(findAlarmMenu(routes,'alarmList',3),null)
- assert.equal(findAlarmMenu([{path:'alarmList',query:'invalid'}],'alarmList',2),null)
- })
- test('menu navigation reads the real permission store sidebar property',async()=>{
- const scope=await readFile('src/views/subSystem/alarm/useAlarmScope.js','utf8')
- const store=await readFile('src/store/modules/permission.js','utf8')
- const property=scope.match(/findAlarmMenu\(permissions\.(\w+)/)[1]
- assert.match(store,new RegExp(property+': \\[\\]'))
- })
- test('raw series preserve zero and missing values and break large sampling gaps',()=>{
- const points=seriesPoints([{time:'2026-01-01 00:00:00',value:0},{time:'2026-01-01 00:05:00',value:2},{time:'2026-01-01 00:10:00',value:null},{time:'2026-01-01 01:00:00',value:4}],true)
- assert.deepEqual(points.map(p=>p[1]),[0,2,null,null,4])
- assert.equal(seriesPoints([{time:'2026-01-01 00:00:00',value:0}],true)[0][1],0)
- })
- test('new GIS entry has the correct name, three tabs and no surrounding environment tab',async()=>{
- const source=await readFile('src/views/subSystem/alarm/drainage/alarmGIS.vue','utf8')
- assert.ok(source.includes('DrainageAlarmGIS')); assert.ok(!source.includes('周边环境'))
- for(const label of ['设备信息','报警记录','监测曲线'])assert.ok(source.includes('label="'+label+'"'))
- await assert.rejects(access('src/views/subSystem/alarm/drainage/alarmMap.vue'))
- })
- test('GIS prototype layout keeps the map width stable with a separate responsive detail drawer',async()=>{
- const source=await readFile('src/views/subSystem/alarm/drainage/alarmGIS.vue','utf8')
- const styles=await readFile('src/views/subSystem/alarm/drainage/alarmGIS.scss','utf8')
- for(const text of ['gis-page-heading','gis-level-metrics','gis-device-pane','gis-map-status','gis-map-legend','gis-workspace-footer'])assert.ok(source.includes(text))
- assert.ok(source.includes('<el-drawer v-model="detailVisible"'))
- assert.ok(source.includes('append-to-body destroy-on-close'))
- assert.ok(!source.includes('selection-panel'))
- assert.ok(source.includes('device.alarmLevel === Number(item.value)'))
- assert.ok(source.includes('getAlarmMap({ ...scope.value'))
- assert.ok(source.includes('pageNum: historyPage.value, pageSize: historySize.value'))
- assert.ok(source.includes('aria-label="查看全部点位"'))
- assert.ok(styles.includes('grid-template-columns: 300px minmax(0, 1fr)'))
- assert.ok(styles.includes('@media (max-width: 680px)'))
- })
- test('ledger uses first-column business number, server pagination and complete-day dates',async()=>{
- const source=await readFile('src/views/subSystem/alarm/list.vue','utf8')
- assert.match(source,/<el-table-column prop="alarmNo" label="报警单号"/)
- assert.ok(source.indexOf('prop="alarmNo"')<source.indexOf('prop="deviceCode"'))
- assert.ok(source.includes('pageNum: pageNum.value, pageSize: pageSize.value'))
- assert.deepEqual(rangeParams(['2026-01-01','2026-01-02']),{startTime:'2026-01-01 00:00:00',endTime:'2026-01-02 23:59:59.999999'})
- })
- test('replaced subsystem entries are removed',async()=>{
- for(const name of ['drainage/jcbj/bjtx.vue','drainage/jcbj/bjyp.vue','drainage/jcbj/bjqd.vue','drainage/jcbj/dqbj.vue','waterSupply/monitorAlarm/index.vue','waterSupply/wjcbj/bjgl.vue']) await assert.rejects(access('src/views/subSystem/'+name))
- })
|