2026-05-15 16:36:50 +08:00
/* eslint-env node */
import fs from 'node:fs'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
const currentDir = path . dirname ( fileURLToPath ( import . meta . url ) )
2026-05-18 08:46:42 +08:00
const pageFile = path . join ( currentDir , '..' , 'index.vue' )
const apiFile = path . resolve ( currentDir , '../../../../api/steady/steadyDataView/index.ts' )
const interfaceFile = path . resolve ( currentDir , '../../../../api/steady/steadyDataView/interface/index.ts' )
const componentDir = path . join ( currentDir , '..' , 'components' )
const utilsDir = path . join ( currentDir , '..' , 'utils' )
2026-05-15 16:36:50 +08:00
const read = file => fs . readFileSync ( file , 'utf8' )
const pageSource = read ( pageFile )
const apiSource = read ( apiFile )
const interfaceSource = read ( interfaceFile )
const componentSource = fs . existsSync ( componentDir )
? fs
. readdirSync ( componentDir )
. filter ( file => file . endsWith ( '.vue' ) )
. map ( file => read ( path . join ( componentDir , file ) ) )
. join ( '\n' )
: ''
const utilitySource = fs . existsSync ( utilsDir )
? fs
. readdirSync ( utilsDir )
. filter ( file => file . endsWith ( '.ts' ) )
. map ( file => read ( path . join ( utilsDir , file ) ) )
. join ( '\n' )
: ''
const expectations = [
2026-05-18 08:46:42 +08:00
[ 'page imports extracted trend workbench' , /SteadyTrendWorkbench/ ] ,
[ 'trend workbench component exists' , fs . existsSync ( path . join ( componentDir , 'SteadyTrendWorkbench.vue' ) ) ] ,
[ 'floating indicator panel component exists' , fs . existsSync ( path . join ( componentDir , 'SteadyIndicatorFloatingPanel.vue' ) ) ] ,
[ 'components import ledger tree panel' , /SteadyLedgerTree/ ] ,
[ 'components import indicator tree panel' , /SteadyIndicatorTree/ ] ,
[ 'components import trend toolbar' , /SteadyTrendToolbar/ ] ,
[ 'components import trend chart panel' , /SteadyTrendChartPanel/ ] ,
2026-05-15 16:36:50 +08:00
[ 'page does not import trend summary panel' , /SteadyTrendSummaryPanel/ ] ,
[ 'page does not import data table panel' , /SteadyDataTablePanel/ ] ,
2026-05-18 08:46:42 +08:00
[ 'components render floating indicator panel' , /indicator-floating-panel/ ] ,
2026-05-15 16:36:50 +08:00
[ 'page defaults floating indicator panel expanded' , /indicatorPanelCollapsed\s*=\s*ref\(false\)/ ] ,
2026-05-27 08:06:12 +08:00
[ 'page restores default harmonic order when harmonic filter becomes visible' , /DEFAULT_HARMONIC_ORDERS[\s\S]*showHarmonicOrders\.value[\s\S]*trendForm\.value\.harmonicOrders\.length[\s\S]*DEFAULT_HARMONIC_ORDERS/ ] ,
2026-05-15 16:36:50 +08:00
[ 'API exposes ledger tree endpoint' , /\/steady\/data-view\/ledger-tree/ ] ,
[ 'API exposes indicator tree endpoint' , /\/steady\/data-view\/indicator-tree/ ] ,
[ 'API exposes trend query endpoint' , /\/steady\/data-view\/trend\/query/ ] ,
[ 'API exposes trend day endpoint' , /\/steady\/data-view\/trend\/day/ ] ,
2026-05-20 08:32:24 +08:00
[ 'API disables global loading for trend query' , /querySteadyTrend[\s\S]*\/steady\/data-view\/trend\/query[\s\S]*loading:\s*false/ ] ,
[ 'API disables global loading for trend day query' , /querySteadyTrendDay[\s\S]*\/steady\/data-view\/trend\/day[\s\S]*loading:\s*false/ ] ,
2026-05-15 16:36:50 +08:00
[ 'API does not expose trend summary endpoint' , /\/steady\/data-view\/trend\/summary/ ] ,
[ 'interfaces define trend query params' , /interface\s+SteadyTrendQueryParams/ ] ,
[ 'interfaces define trend series' , /interface\s+SteadyTrendSeries/ ] ,
[ 'interfaces do not define trend summary' , /interface\s+SteadyTrendSummary/ ] ,
[ 'components render ledger checkbox tree' , /show-checkbox[\s\S]*@check/ ] ,
[ 'components render indicator checkbox tree' , /indicator-tree[\s\S]*show-checkbox[\s\S]*@check/ ] ,
[ 'components reuse LineChart' , /<LineChart/ ] ,
[ 'toolbar uses shared time period search' , /TimePeriodSearch/ ] ,
2026-05-27 08:06:12 +08:00
[ 'toolbar labels stat quality filters' , /toolbar-field__label[\s\S]*统计:[\s\S]*toolbar-field__label[\s\S]*数据质量:/ ] ,
2026-05-18 16:30:02 +08:00
[ 'toolbar does not render bucket selector' , /modelValue\.bucket|bucketOptions|粒度:|选择时间粒度/ ] ,
2026-05-18 08:46:42 +08:00
[ 'toolbar does not render phase selector' , /modelValue\.phases|phaseOptions|resolvePhaseLabel/ ] ,
2026-05-27 08:06:12 +08:00
[ 'toolbar renders quality flag with switch' , /<el-switch[\s\S]*@update:model-value="handleQualityFlagChange"/ ] ,
[ 'toolbar maps valid quality flag to zero' , /active-text="有效"[\s\S]*:active-value="0"/ ] ,
2026-05-18 16:30:02 +08:00
[ 'utilities default to valid quality flag zero' , /qualityFlag:\s*0/ ] ,
2026-05-27 08:06:12 +08:00
[ 'utilities default harmonic order to second harmonic' , /DEFAULT_HARMONIC_ORDERS\s*=\s*\[2\]/ ] ,
2026-05-15 16:36:50 +08:00
[ 'utilities collect selected line ids' , /export const collectSelectedLineIds/ ] ,
[ 'utilities validate selection limits' , /export const validateTrendSelection[\s\S]*24/ ] ,
2026-05-18 08:46:42 +08:00
[ 'utilities do not require phase selection' , /if\s*\(!phases\.length\)/ ] ,
2026-05-27 08:06:12 +08:00
[ 'utilities cap harmonic order count at three' , /MAX_HARMONIC_ORDER_COUNT\s*=\s*3/ ] ,
[ 'utilities validate harmonic orders' , /export const validateHarmonicOrders[\s\S]*最多选择 \$\{MAX_HARMONIC_ORDER_COUNT\} 个/ ] ,
[ 'utilities count harmonic orders as one indicator in selection estimates' , /const harmonicMultiplier\s*=\s*1/ ] ,
[
'toolbar does not provide harmonic quick groups' ,
/HARMONIC_ORDER_QUICK_GROUPS|harmonic-select__quick|appendHarmonicQuickOrders/
] ,
[
'toolbar warns when harmonic selection exceeds three instead of using silent multiple-limit' ,
/(?=[\s\S]*MAX_HARMONIC_ORDER_COUNT)(?=[\s\S]*ElMessage\.warning\(`谐波次数最多选择 \$\{MAX_HARMONIC_ORDER_COUNT\} 个`\))(?![\s\S]*multiple-limit)/
] ,
2026-05-15 16:36:50 +08:00
[ 'utilities build trend query payload' , /export const buildSteadyTrendQueryPayload/ ] ,
2026-05-18 08:46:42 +08:00
[ 'utilities strip milliseconds from trend query time' , /formatSteadyTrendQueryTime[\s\S]*replace\(\s*\/\\\.\[\^.\]\+\$\// ] ,
2026-05-18 16:30:02 +08:00
[ 'utilities do not send bucket in trend query payload' , /bucket:\s*formState\.bucket/ ] ,
2026-05-18 08:46:42 +08:00
[ 'utilities do not send phases in trend query payload' , /phases:\s*formState\.phases/ ] ,
2026-05-18 16:30:02 +08:00
[ 'trend query params do not include bucket' , /interface\s+SteadyTrendQueryParams\s*{[^}]*bucket\??:\s*string/ ] ,
2026-05-18 08:46:42 +08:00
[ 'trend query params do not include phases' , /phases:\s*string\[\]/ ] ,
2026-05-15 16:36:50 +08:00
[ 'utilities build chart options' , /export const buildSteadyTrendChartOptions/ ]
]
const sourceByExpectation = [
pageSource ,
2026-05-18 08:46:42 +08:00
componentSource ,
componentSource ,
componentSource ,
componentSource ,
componentSource ,
componentSource ,
componentSource ,
2026-05-15 16:36:50 +08:00
pageSource ,
2026-05-18 08:46:42 +08:00
componentSource ,
2026-05-15 16:36:50 +08:00
pageSource ,
2026-05-27 08:06:12 +08:00
pageSource ,
2026-05-15 16:36:50 +08:00
apiSource ,
apiSource ,
apiSource ,
2026-05-20 08:32:24 +08:00
apiSource ,
apiSource ,
2026-05-15 16:36:50 +08:00
apiSource ,
apiSource ,
interfaceSource ,
interfaceSource ,
interfaceSource ,
componentSource ,
componentSource ,
componentSource ,
componentSource ,
componentSource ,
componentSource ,
componentSource ,
2026-05-18 16:30:02 +08:00
componentSource ,
componentSource ,
2026-05-15 16:36:50 +08:00
utilitySource ,
utilitySource ,
utilitySource ,
utilitySource ,
2026-05-18 08:46:42 +08:00
utilitySource ,
utilitySource ,
utilitySource ,
2026-05-18 16:30:02 +08:00
utilitySource ,
2026-05-27 08:06:12 +08:00
componentSource ,
componentSource ,
utilitySource ,
utilitySource ,
2026-05-18 16:30:02 +08:00
utilitySource ,
interfaceSource ,
2026-05-18 08:46:42 +08:00
interfaceSource ,
2026-05-27 08:06:12 +08:00
utilitySource ,
2026-05-15 16:36:50 +08:00
utilitySource
]
const failures = expectations . filter ( ( [ name , pattern ] , index ) => {
2026-05-18 08:46:42 +08:00
const matched = typeof pattern === 'boolean' ? pattern : pattern . test ( sourceByExpectation [ index ] )
2026-05-15 16:36:50 +08:00
return name . includes ( 'does not' ) || name . includes ( 'do not' ) ? matched : ! matched
} )
if ( failures . length ) {
console . error ( 'steadyDataView trend contract failed:' )
for ( const [ name ] of failures ) {
console . error ( ` - ${ name } ` )
}
process . exit ( 1 )
}
console . log ( 'steadyDataView trend contract passed' )