|
|
@@ -2,7 +2,14 @@ import test from 'node:test'
|
|
|
import assert from 'node:assert/strict'
|
|
|
import fs from 'node:fs'
|
|
|
|
|
|
-import { buildPipeNetworkStatisticsQuery } from '../src/utils/waterFacilityStatisticsModel.js'
|
|
|
+import {
|
|
|
+ buildPipeNetworkStatisticsQuery,
|
|
|
+ FACILITY_SECTION_LABELS,
|
|
|
+ FACILITY_SECTION_TYPES,
|
|
|
+ FACILITY_STATUS_TAG_TYPES,
|
|
|
+ getFacilityStatusOptions,
|
|
|
+ resolveFacilityStatusLabel
|
|
|
+} from '../src/utils/waterFacilityStatisticsModel.js'
|
|
|
|
|
|
test('statistics query mirrors dashboard and export filters', () => {
|
|
|
assert.deepEqual(
|
|
|
@@ -14,6 +21,11 @@ test('statistics query mirrors dashboard and export filters', () => {
|
|
|
)
|
|
|
})
|
|
|
|
|
|
+test('statistics status filter returns to the all-state contract', () => {
|
|
|
+ assert.equal(buildPipeNetworkStatisticsQuery({ facilityTypes: ['pipe'], status: '' }).status, undefined)
|
|
|
+ assert.equal(buildPipeNetworkStatisticsQuery({ facilityTypes: ['pipe'], status: null }).status, undefined)
|
|
|
+})
|
|
|
+
|
|
|
test('statistics query omits empty filters instead of requesting empty datasets', () => {
|
|
|
assert.deepEqual(buildPipeNetworkStatisticsQuery(), {})
|
|
|
assert.deepEqual(buildPipeNetworkStatisticsQuery({ facilityTypes: ['', ' '], status: '' }), {})
|
|
|
@@ -37,6 +49,152 @@ test('statistics status filter follows the facility status contract', () => {
|
|
|
'utf8'
|
|
|
)
|
|
|
|
|
|
- assert.match(source, /<el-option label="正常" value="0" \/><el-option label="异常" value="1" \/>/u)
|
|
|
- assert.doesNotMatch(source, /value="2"/u)
|
|
|
+ assert.match(source, /getFacilityStatusOptions/u)
|
|
|
+ assert.match(source, /resolveFacilityStatusLabel/u)
|
|
|
+ assert.match(source, /label="展示全部" value=""/u)
|
|
|
+ assert.doesNotMatch(source, /@clear="/u)
|
|
|
+ assert.match(source, /warningCount/u)
|
|
|
+ assert.match(source, /unknownCount/u)
|
|
|
+})
|
|
|
+
|
|
|
+test('statistics table uses Chinese field labels and explicit hints', () => {
|
|
|
+ const source = fs.readFileSync(
|
|
|
+ 'src/views/subSystem/waterSupply/components/WaterFacilityDashboard.vue',
|
|
|
+ 'utf8'
|
|
|
+ )
|
|
|
+
|
|
|
+ assert.match(source, /STATISTIC_LABELS/u)
|
|
|
+ assert.match(source, /el-tooltip content="按设施类型汇总的关键统计指标"/u)
|
|
|
+ assert.match(source, /el-tooltip content="当前行的统计口径和数据来源"/u)
|
|
|
+})
|
|
|
+
|
|
|
+test('statistics status rows use the agreed unknown label', () => {
|
|
|
+ const source = fs.readFileSync(
|
|
|
+ 'src/views/subSystem/waterSupply/components/WaterFacilityDashboard.vue',
|
|
|
+ 'utf8'
|
|
|
+ )
|
|
|
+
|
|
|
+ assert.match(source, /resolveFacilityStatusLabel\(facilityType, item\.name\)/u)
|
|
|
+ assert.match(source, /category === 'normal' \? '正常' : '未知'/u)
|
|
|
+ assert.doesNotMatch(source, /未填报/u)
|
|
|
+})
|
|
|
+
|
|
|
+test('statistics status filter returns to the unfiltered contract', () => {
|
|
|
+ assert.deepEqual(
|
|
|
+ buildPipeNetworkStatisticsQuery({ facilityTypes: ['plant'], status: null }),
|
|
|
+ { facilityTypes: 'plant' }
|
|
|
+ )
|
|
|
+ assert.doesNotMatch(
|
|
|
+ JSON.stringify(buildPipeNetworkStatisticsQuery({ facilityTypes: ['plant'], status: '' })),
|
|
|
+ /"status"/u
|
|
|
+ )
|
|
|
+})
|
|
|
+
|
|
|
+test('facility status labels follow the per-facility-type contract', () => {
|
|
|
+ assert.equal(resolveFacilityStatusLabel('source', 0), '正常')
|
|
|
+ assert.equal(resolveFacilityStatusLabel('source', 1), '异常')
|
|
|
+ assert.equal(resolveFacilityStatusLabel('plant', 1), '停产')
|
|
|
+ assert.equal(resolveFacilityStatusLabel('pumpStation', 1), '故障停运')
|
|
|
+ assert.equal(resolveFacilityStatusLabel('pumpStation', 2), '检修中')
|
|
|
+ assert.equal(resolveFacilityStatusLabel('pipe', 1), '停用')
|
|
|
+ assert.equal(resolveFacilityStatusLabel('user', 1), '欠费')
|
|
|
+ assert.equal(resolveFacilityStatusLabel('user', 2), '停用')
|
|
|
+ assert.equal(resolveFacilityStatusLabel('source', null), '未知')
|
|
|
+})
|
|
|
+
|
|
|
+test('dashboard sections map to the shared facility status contract', () => {
|
|
|
+ assert.deepEqual(FACILITY_SECTION_TYPES, {
|
|
|
+ pipeNetwork: 'pipe',
|
|
|
+ waterPlant: 'plant',
|
|
|
+ waterSource: 'source',
|
|
|
+ waterUser: 'user',
|
|
|
+ pumpStation: 'pumpStation'
|
|
|
+ })
|
|
|
+
|
|
|
+ assert.equal(resolveFacilityStatusLabel(FACILITY_SECTION_TYPES.waterSource, 1), '异常')
|
|
|
+ assert.equal(resolveFacilityStatusLabel(FACILITY_SECTION_TYPES.waterPlant, 1), '停产')
|
|
|
+ assert.equal(resolveFacilityStatusLabel(FACILITY_SECTION_TYPES.pipeNetwork, 1), '停用')
|
|
|
+ assert.equal(resolveFacilityStatusLabel(FACILITY_SECTION_TYPES.waterUser, 1), '欠费')
|
|
|
+ assert.deepEqual(FACILITY_SECTION_LABELS, {
|
|
|
+ pipeNetwork: '管网',
|
|
|
+ waterPlant: '水厂',
|
|
|
+ waterSource: '水源地',
|
|
|
+ waterUser: '用水户',
|
|
|
+ pumpStation: '泵站'
|
|
|
+ })
|
|
|
+})
|
|
|
+
|
|
|
+test('facility status colors follow the per-facility-type contract', () => {
|
|
|
+ assert.deepEqual(FACILITY_STATUS_TAG_TYPES.source, { 0: 'success', 1: 'danger' })
|
|
|
+ assert.deepEqual(FACILITY_STATUS_TAG_TYPES.plant, { 0: 'success', 1: 'danger' })
|
|
|
+ assert.deepEqual(FACILITY_STATUS_TAG_TYPES.pumpStation, {
|
|
|
+ 0: 'success',
|
|
|
+ 1: 'danger',
|
|
|
+ 2: 'warning'
|
|
|
+ })
|
|
|
+ assert.deepEqual(FACILITY_STATUS_TAG_TYPES.pipe, { 0: 'success', 1: 'danger' })
|
|
|
+ assert.deepEqual(FACILITY_STATUS_TAG_TYPES.user, {
|
|
|
+ 0: 'success',
|
|
|
+ 1: 'warning',
|
|
|
+ 2: 'danger'
|
|
|
+ })
|
|
|
+})
|
|
|
+
|
|
|
+test('status options only expose values valid for selected facility types', () => {
|
|
|
+ assert.deepEqual(getFacilityStatusOptions(['source']), [
|
|
|
+ { value: '0', label: '水源地: 正常' },
|
|
|
+ { value: '1', label: '水源地: 异常' }
|
|
|
+ ])
|
|
|
+ assert.deepEqual(getFacilityStatusOptions(['source', 'pumpStation', 'user']), [
|
|
|
+ { value: '0', label: '正常' },
|
|
|
+ { value: '1', label: '异常 / 故障停运 / 欠费' },
|
|
|
+ { value: '2', label: '检修中 / 停用' }
|
|
|
+ ])
|
|
|
+ assert.deepEqual(getFacilityStatusOptions([]), [])
|
|
|
+})
|
|
|
+
|
|
|
+test('dashboard provides an explicit path back to the unfiltered state', () => {
|
|
|
+ const source = fs.readFileSync(
|
|
|
+ 'src/views/subSystem/waterSupply/components/WaterFacilityDashboard.vue',
|
|
|
+ 'utf8'
|
|
|
+ )
|
|
|
+
|
|
|
+ assert.match(source, /@change="handleStatusChange"/u)
|
|
|
+ assert.match(source, /statusFilter\.value = String\(value \?\? ''\)/u)
|
|
|
+})
|
|
|
+
|
|
|
+test('changing facility types normalizes a stale status filter before loading data', () => {
|
|
|
+ const source = fs.readFileSync(
|
|
|
+ 'src/views/subSystem/waterSupply/components/WaterFacilityDashboard.vue',
|
|
|
+ 'utf8'
|
|
|
+ )
|
|
|
+
|
|
|
+ assert.match(source, /@change="handleTypeChange"/u)
|
|
|
+ assert.match(
|
|
|
+ source,
|
|
|
+ /const handleTypeChange = \(\) => \{\s*const options = getFacilityStatusOptions\(selectedTypes\.value\)\s*if \(!options\.some\(option => option\.value === statusFilter\.value\)\) statusFilter\.value = ''\s*loadData\(\)\s*\}/u
|
|
|
+ )
|
|
|
+})
|
|
|
+
|
|
|
+test('statistics cards and charts use a non-overlapping technical layout', () => {
|
|
|
+ const source = fs.readFileSync(
|
|
|
+ 'src/views/subSystem/waterSupply/components/WaterFacilityDashboard.vue',
|
|
|
+ 'utf8'
|
|
|
+ )
|
|
|
+
|
|
|
+ assert.match(source, /\.metric-icon \{\s*[^}]*position: static/u)
|
|
|
+ assert.match(source, /grid-template-columns: repeat\(5, minmax\(160px, 1fr\)\)/u)
|
|
|
+ assert.match(source, /radius: \['52%', '72%'\]/u)
|
|
|
+ assert.doesNotMatch(source, /roseType:/u)
|
|
|
+})
|
|
|
+
|
|
|
+test('statistics charts hide status series without data', () => {
|
|
|
+ const source = fs.readFileSync(
|
|
|
+ 'src/views/subSystem/waterSupply/components/WaterFacilityDashboard.vue',
|
|
|
+ 'utf8'
|
|
|
+ )
|
|
|
+
|
|
|
+ assert.match(source, /\.filter\(series => facilityOverview\.some\(item => Number\(item\[series\.key\] \|\| 0\) > 0\)\)/u)
|
|
|
+ assert.match(source, /resolveFacilityStatusLabel\(facilityType, item\.name\)/u)
|
|
|
+ assert.doesNotMatch(source, /未填报/u)
|
|
|
})
|