waterFacilityStatistics.test.mjs 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. import test from 'node:test'
  2. import assert from 'node:assert/strict'
  3. import fs from 'node:fs'
  4. import {
  5. buildPipeNetworkStatisticsQuery,
  6. FACILITY_SECTION_LABELS,
  7. FACILITY_SECTION_TYPES,
  8. FACILITY_STATUS_TAG_TYPES,
  9. getFacilityStatusOptions,
  10. resolveFacilityStatusLabel
  11. } from '../src/utils/waterFacilityStatisticsModel.js'
  12. test('statistics query mirrors dashboard and export filters', () => {
  13. assert.deepEqual(
  14. buildPipeNetworkStatisticsQuery({
  15. facilityTypes: ['plant', 'pipe', ''],
  16. status: '1'
  17. }),
  18. { facilityTypes: 'plant,pipe', status: '1' }
  19. )
  20. })
  21. test('statistics status filter returns to the all-state contract', () => {
  22. assert.equal(buildPipeNetworkStatisticsQuery({ facilityTypes: ['pipe'], status: '' }).status, undefined)
  23. assert.equal(buildPipeNetworkStatisticsQuery({ facilityTypes: ['pipe'], status: null }).status, undefined)
  24. })
  25. test('statistics query omits empty filters instead of requesting empty datasets', () => {
  26. assert.deepEqual(buildPipeNetworkStatisticsQuery(), {})
  27. assert.deepEqual(buildPipeNetworkStatisticsQuery({ facilityTypes: ['', ' '], status: '' }), {})
  28. })
  29. test('statistics page prevents empty facility-type requests and export', () => {
  30. const source = fs.readFileSync(
  31. 'src/views/subSystem/waterSupply/components/WaterFacilityDashboard.vue',
  32. 'utf8'
  33. )
  34. assert.match(source, /buildPipeNetworkStatisticsQuery/u)
  35. assert.match(source, /:disabled="!selectedTypes\.length"/u)
  36. assert.match(source, /请至少选择一类设施/u)
  37. assert.match(source, /if \(!selectedTypes\.value\.length\)/u)
  38. })
  39. test('statistics status filter follows the facility status contract', () => {
  40. const source = fs.readFileSync(
  41. 'src/views/subSystem/waterSupply/components/WaterFacilityDashboard.vue',
  42. 'utf8'
  43. )
  44. assert.match(source, /getFacilityStatusOptions/u)
  45. assert.match(source, /resolveFacilityStatusLabel/u)
  46. assert.match(source, /label="展示全部" value=""/u)
  47. assert.doesNotMatch(source, /@clear="/u)
  48. assert.match(source, /warningCount/u)
  49. assert.match(source, /unknownCount/u)
  50. })
  51. test('statistics table uses Chinese field labels and explicit hints', () => {
  52. const source = fs.readFileSync(
  53. 'src/views/subSystem/waterSupply/components/WaterFacilityDashboard.vue',
  54. 'utf8'
  55. )
  56. assert.match(source, /STATISTIC_LABELS/u)
  57. assert.match(source, /el-tooltip content="按设施类型汇总的关键统计指标"/u)
  58. assert.match(source, /el-tooltip content="当前行的统计口径和数据来源"/u)
  59. })
  60. test('statistics status rows use the agreed unknown label', () => {
  61. const source = fs.readFileSync(
  62. 'src/views/subSystem/waterSupply/components/WaterFacilityDashboard.vue',
  63. 'utf8'
  64. )
  65. assert.match(source, /resolveFacilityStatusLabel\(facilityType, item\.name\)/u)
  66. assert.match(source, /category === 'normal' \? '正常' : '未知'/u)
  67. assert.doesNotMatch(source, /未填报/u)
  68. })
  69. test('statistics status filter returns to the unfiltered contract', () => {
  70. assert.deepEqual(
  71. buildPipeNetworkStatisticsQuery({ facilityTypes: ['plant'], status: null }),
  72. { facilityTypes: 'plant' }
  73. )
  74. assert.doesNotMatch(
  75. JSON.stringify(buildPipeNetworkStatisticsQuery({ facilityTypes: ['plant'], status: '' })),
  76. /"status"/u
  77. )
  78. })
  79. test('facility status labels follow the per-facility-type contract', () => {
  80. assert.equal(resolveFacilityStatusLabel('source', 0), '正常')
  81. assert.equal(resolveFacilityStatusLabel('source', 1), '异常')
  82. assert.equal(resolveFacilityStatusLabel('plant', 1), '停产')
  83. assert.equal(resolveFacilityStatusLabel('pumpStation', 1), '故障停运')
  84. assert.equal(resolveFacilityStatusLabel('pumpStation', 2), '检修中')
  85. assert.equal(resolveFacilityStatusLabel('pipe', 1), '停用')
  86. assert.equal(resolveFacilityStatusLabel('user', 1), '欠费')
  87. assert.equal(resolveFacilityStatusLabel('user', 2), '停用')
  88. assert.equal(resolveFacilityStatusLabel('source', null), '未知')
  89. })
  90. test('dashboard sections map to the shared facility status contract', () => {
  91. assert.deepEqual(FACILITY_SECTION_TYPES, {
  92. pipeNetwork: 'pipe',
  93. waterPlant: 'plant',
  94. waterSource: 'source',
  95. waterUser: 'user',
  96. pumpStation: 'pumpStation'
  97. })
  98. assert.equal(resolveFacilityStatusLabel(FACILITY_SECTION_TYPES.waterSource, 1), '异常')
  99. assert.equal(resolveFacilityStatusLabel(FACILITY_SECTION_TYPES.waterPlant, 1), '停产')
  100. assert.equal(resolveFacilityStatusLabel(FACILITY_SECTION_TYPES.pipeNetwork, 1), '停用')
  101. assert.equal(resolveFacilityStatusLabel(FACILITY_SECTION_TYPES.waterUser, 1), '欠费')
  102. assert.deepEqual(FACILITY_SECTION_LABELS, {
  103. pipeNetwork: '管网',
  104. waterPlant: '水厂',
  105. waterSource: '水源地',
  106. waterUser: '用水户',
  107. pumpStation: '泵站'
  108. })
  109. })
  110. test('facility status colors follow the per-facility-type contract', () => {
  111. assert.deepEqual(FACILITY_STATUS_TAG_TYPES.source, { 0: 'success', 1: 'danger' })
  112. assert.deepEqual(FACILITY_STATUS_TAG_TYPES.plant, { 0: 'success', 1: 'danger' })
  113. assert.deepEqual(FACILITY_STATUS_TAG_TYPES.pumpStation, {
  114. 0: 'success',
  115. 1: 'danger',
  116. 2: 'warning'
  117. })
  118. assert.deepEqual(FACILITY_STATUS_TAG_TYPES.pipe, { 0: 'success', 1: 'danger' })
  119. assert.deepEqual(FACILITY_STATUS_TAG_TYPES.user, {
  120. 0: 'success',
  121. 1: 'warning',
  122. 2: 'danger'
  123. })
  124. })
  125. test('status options only expose values valid for selected facility types', () => {
  126. assert.deepEqual(getFacilityStatusOptions(['source']), [
  127. { value: '0', label: '水源地: 正常' },
  128. { value: '1', label: '水源地: 异常' }
  129. ])
  130. assert.deepEqual(getFacilityStatusOptions(['source', 'pumpStation', 'user']), [
  131. { value: '0', label: '正常' },
  132. { value: '1', label: '异常 / 故障停运 / 欠费' },
  133. { value: '2', label: '检修中 / 停用' }
  134. ])
  135. assert.deepEqual(getFacilityStatusOptions([]), [])
  136. })
  137. test('dashboard provides an explicit path back to the unfiltered state', () => {
  138. const source = fs.readFileSync(
  139. 'src/views/subSystem/waterSupply/components/WaterFacilityDashboard.vue',
  140. 'utf8'
  141. )
  142. assert.match(source, /@change="handleStatusChange"/u)
  143. assert.match(source, /statusFilter\.value = String\(value \?\? ''\)/u)
  144. })
  145. test('changing facility types normalizes a stale status filter before loading data', () => {
  146. const source = fs.readFileSync(
  147. 'src/views/subSystem/waterSupply/components/WaterFacilityDashboard.vue',
  148. 'utf8'
  149. )
  150. assert.match(source, /@change="handleTypeChange"/u)
  151. assert.match(
  152. source,
  153. /const handleTypeChange = \(\) => \{\s*const options = getFacilityStatusOptions\(selectedTypes\.value\)\s*if \(!options\.some\(option => option\.value === statusFilter\.value\)\) statusFilter\.value = ''\s*loadData\(\)\s*\}/u
  154. )
  155. })
  156. test('statistics cards and charts use a non-overlapping technical layout', () => {
  157. const source = fs.readFileSync(
  158. 'src/views/subSystem/waterSupply/components/WaterFacilityDashboard.vue',
  159. 'utf8'
  160. )
  161. assert.match(source, /\.metric-icon \{\s*[^}]*position: static/u)
  162. assert.match(source, /grid-template-columns: repeat\(5, minmax\(160px, 1fr\)\)/u)
  163. assert.match(source, /radius: \['52%', '72%'\]/u)
  164. assert.doesNotMatch(source, /roseType:/u)
  165. })
  166. test('statistics charts hide status series without data', () => {
  167. const source = fs.readFileSync(
  168. 'src/views/subSystem/waterSupply/components/WaterFacilityDashboard.vue',
  169. 'utf8'
  170. )
  171. assert.match(source, /\.filter\(series => facilityOverview\.some\(item => Number\(item\[series\.key\] \|\| 0\) > 0\)\)/u)
  172. assert.match(source, /resolveFacilityStatusLabel\(facilityType, item\.name\)/u)
  173. assert.doesNotMatch(source, /未填报/u)
  174. })