/* 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)) const viewDir = path.join(currentDir, '..') const read = file => fs.readFileSync(path.join(viewDir, file), 'utf8') const toolbarSource = read('components/SteadyTrendToolbar.vue') const chartPanelSource = read('components/SteadyTrendChartPanel.vue') const trendPayloadSource = read('utils/trendPayload.ts') const trendOptionsSource = read('utils/trendOptions.ts') const selectionRulesSource = read('utils/selectionRules.ts') const sharedPhaseColorSource = fs.readFileSync(path.join(viewDir, '..', '..', '..', 'utils', 'phaseColors.ts'), 'utf8') const sharedStyleSource = fs.readFileSync(path.join(viewDir, '..', '..', '..', 'styles', 'var.scss'), 'utf8') const expectations = [ [ 'toolbar uses a single active stat type', /:model-value="modelValue\.statType"[\s\S]*@update:model-value="updateField\('statType', \$event\)/, toolbarSource ], [ 'toolbar no longer allows multiple stat types in one query', /\s*MAX_SELECTED_LINE_COUNT/, selectionRulesSource ], [ 'selection rules cap indicators at six', /MAX_SELECTED_INDICATOR_COUNT\s*=\s*6[\s\S]*indicators\.length\s*>\s*MAX_SELECTED_INDICATOR_COUNT/, selectionRulesSource ], [ 'chart panel renders a list of grouped charts', /v-for="group in chartGroups"[\s\S]*:options="group\.options"/, chartPanelSource ], [ 'chart panel syncs grouped charts through LineChart group and dataZoom events', /\(\{\s*\.\.\.DEFAULT_STEADY_TREND_X_ZOOM_RANGE\s*\}\)/, chartPanelSource ], [ 'chart panel compares reset state against the default x zoom range', /const isDefaultTrendXZoomRange[\s\S]*DEFAULT_STEADY_TREND_X_ZOOM_RANGE[\s\S]*const canResetTrendChart[\s\S]*!isDefaultTrendXZoomRange\.value/, chartPanelSource ], [ 'chart panel stores y zoom scale and active chart tool', /const trendYZoomScale\s*=\s*ref\(1\)[\s\S]*const activeTrendInteractionMode\s*=\s*ref\('none'\)/, chartPanelSource ], [ 'chart panel passes active chart tool and y zoom scale into grouped options', /buildSteadyTrendChartGroups\([^)]*trendXZoomRange\.value[\s\S]*activeTool:\s*activeTrendInteractionMode\.value[\s\S]*yZoomScale:\s*trendYZoomScale\.value/, chartPanelSource ], [ 'chart panel resets x zoom range to the default first tenth when trend result changes', /const\s+resetTrendToolState\s*=\s*\(\)\s*=>\s*\{[\s\S]*trendXZoomRange\.value\s*=\s*\{\s*\.\.\.DEFAULT_STEADY_TREND_X_ZOOM_RANGE\s*\}[\s\S]*watch\(\s*\(\)\s*=>\s*props\.trendResult[\s\S]*resetTrendToolState\(\)/, chartPanelSource ], [ 'chart panel resets toolbar state when trend result changes', /watch\(\s*\(\)\s*=>\s*props\.trendResult[\s\S]*resetTrendToolState\(\)/, chartPanelSource ], [ 'chart panel can export the visible trend charts as an image', /steady-trend-export-target[\s\S]*html2canvas[\s\S]*downloadSteadyTrendImage/, chartPanelSource ], ['chart utilities expose grouped chart options', /export const buildSteadyTrendChartGroups/, trendOptionsSource], [ 'chart utilities carry one shared ECharts group for steady multi-chart sync', /group:\s*STEADY_TREND_CHART_GROUP/, trendOptionsSource ], [ 'chart utilities accept shared x zoom range when building options', /buildSteadyTrendChartOptions\s*=\s*\([^)]*zoomRange:\s*SteadyTrendZoomRange/, trendOptionsSource ], [ 'chart utilities accept active tool and y zoom options', /interface\s+SteadyTrendChartBuildOptions[\s\S]*activeTool\?:[\s\S]*yZoomScale\?:/, trendOptionsSource ], [ 'chart options expose activeTool for LineChart interactions', /activeTool:\s*chartOptions\.activeTool\s*\|\|\s*'none'/, trendOptionsSource ], [ 'chart options apply y zoom scale to steady y axis', /applySteadyYAxisZoom\([\s\S]*buildSteadyTrendAxisConfig\(values,\s*unit\)[\s\S]*chartOptions\.yZoomScale/, trendOptionsSource ], [ 'chart options bind dataZoom to shared x zoom range', /dataZoom:\s*\[[\s\S]*start:\s*zoomRange\.start[\s\S]*end:\s*zoomRange\.end/, trendOptionsSource ], [ 'chart options accept wheel zoom option', /interface\s+SteadyTrendChartBuildOptions[\s\S]*wheelZoomEnabled\?:\s*boolean/, trendOptionsSource ], [ 'chart options only zoom on mouse wheel when enabled', /zoomOnMouseWheel:\s*chartOptions\.wheelZoomEnabled\s*===\s*true[\s\S]*moveOnMouseWheel:\s*false/, trendOptionsSource ], [ 'chart options keep hover axis pointer as a vertical line', /tooltip:\s*\{[\s\S]*axisPointer:\s*\{[\s\S]*type:\s*'line'/, trendOptionsSource ], [ 'chart options calculate visible point count from shared zoom range', /resolveSteadyTrendVisiblePointCount[\s\S]*zoomRange\.end\s*-\s*zoomRange\.start[\s\S]*Math\.ceil/, trendOptionsSource ], [ 'chart series line width uses visible point count after x zoom', /width:\s*resolveSteadyTrendLineWidth\(\s*resolveSteadyTrendVisiblePointCount\(series\.points\?\.length\s*\|\|\s*0,\s*zoomRange\)\s*\)/, trendOptionsSource ], [ 'chart grouping splits by indicator for one monitoring point', /lineIds\.length\s*===\s*1[\s\S]*indicatorCodes\.length\s*>\s*1[\s\S]*'indicator'/, trendOptionsSource ], [ 'chart grouping splits by monitoring point for one indicator', /lineIds\.length\s*>\s*1[\s\S]*indicatorCodes\.length\s*===\s*1[\s\S]*'line'/, trendOptionsSource ], [ 'chart options keep fixed left grid for multi-chart alignment', /left:\s*STEADY_TREND_GRID_LEFT[\s\S]*containLabel:\s*false/, trendOptionsSource ], [ 'chart options use compact top grid to reduce title legend height', /STEADY_TREND_GRID_TOP\s*=\s*28[\s\S]*top:\s*STEADY_TREND_GRID_TOP/, trendOptionsSource ], [ 'chart panel does not render external chart title rows', /^(?![\s\S]*
)[\s\S]*$/, chartPanelSource ], [ 'chart options render centered group title through ECharts title', /title:\s*\{[\s\S]*text:\s*chartTitle[\s\S]*left:\s*'center'[\s\S]*top:\s*0/, trendOptionsSource ], [ 'chart grouping passes resolved title into ECharts options', /const\s+groupTitle\s*=\s*resolveGroupTitle\(groupSeries\)[\s\S]*buildSteadyTrendChartOptions\(groupSeries,\s*zoomRange,\s*isLastChart,\s*chartOptions,\s*groupTitle\)/, trendOptionsSource ], [ 'chart options explicitly show min and max y labels', /showMinLabel:\s*true[\s\S]*showMaxLabel:\s*true/, trendOptionsSource ], [ 'chart y axis keeps waveform unit placement style without changing unit content', /nameLocation:\s*'middle'[\s\S]*nameGap:\s*42[\s\S]*nameTextStyle:/, trendOptionsSource ], [ 'chart x axis only keeps first and last labels like waveform', /buildSteadyTimeAxisLabelFormatter[\s\S]*index\s*!==\s*0\s*&&\s*index\s*!==\s*lastIndex[\s\S]*return\s*''/, trendOptionsSource ], [ 'chart x axis label layout follows waveform except unit display', /axisLabel:\s*\{[\s\S]*hideOverlap:\s*false[\s\S]*interval:\s*0[\s\S]*margin:\s*showTimeAxis\s*\?\s*10\s*:\s*0[\s\S]*formatter:\s*buildSteadyTimeAxisLabelFormatter\(timeLabels\)/, trendOptionsSource ], [ 'chart options allow hiding the x axis for non-final grouped charts', /buildSteadyTrendChartOptions\s*=\s*\([^)]*showTimeAxis\s*=\s*true/, trendOptionsSource ], [ 'chart x axis keeps labels visible but hides baseline and dense ticks', /axisLine:\s*\{[\s\S]*show:\s*false[\s\S]*axisLabel:\s*\{[\s\S]*show:\s*showTimeAxis[\s\S]*axisTick:\s*\{[\s\S]*show:\s*false/, trendOptionsSource ], [ 'chart grouping only shows the x axis on the final grouped chart', /const\s+groupEntries\s*=\s*Array\.from\(groupMap\.entries\(\)\)[\s\S]*const\s+isLastChart\s*=\s*index\s*===\s*groupEntries\.length\s*-\s*1[\s\S]*buildSteadyTrendChartOptions\(groupSeries,\s*zoomRange,\s*isLastChart,\s*chartOptions,\s*groupTitle\)/, trendOptionsSource ], [ 'chart options use waveform line width buckets including the final three widths', /resolveSteadyTrendLineWidth[\s\S]*200000\)\s*return\s*0\.35[\s\S]*100000\)\s*return\s*0\.45[\s\S]*50000\)\s*return\s*0\.55[\s\S]*20000\)\s*return\s*0\.65[\s\S]*10000\)\s*return\s*0\.75[\s\S]*5000\)\s*return\s*0\.9[\s\S]*2000\)\s*return\s*1[\s\S]*800\)\s*return\s*1\.1[\s\S]*200\)\s*return\s*1\.2[\s\S]*return\s*STEADY_TREND_LINE_MAX_WIDTH/, trendOptionsSource ], ['chart options read phase colors from shared theme utility', /resolvePhaseThemeColor/, trendOptionsSource], [ 'chart legend only uses phase name', /const formatSeriesName[\s\S]*return series\.phase \|\| series\.seriesKey/, trendOptionsSource ], [ 'chart group title joins monitoring point and indicator with underscore', /\[firstSeries\.lineName[\s\S]*firstSeries\.indicatorName[\s\S]*\][\s\S]*\.filter\(Boolean\)[\s\S]*\.join\('_'\)/, trendOptionsSource ], [ 'steady y-axis above-one upper padding uses 1.05', /STEADY_AXIS_EXPAND_RATIO_ABOVE_ONE\s*=\s*1\.05/, trendOptionsSource ], [ 'steady y-axis above-one lower padding uses 0.95', /STEADY_AXIS_SHRINK_RATIO_ABOVE_ONE\s*=\s*0\.95/, trendOptionsSource ], [ 'steady y-axis compact padding uses 1.015 for narrow above-one ranges', /STEADY_AXIS_COMPACT_EXPAND_RATIO\s*=\s*1\.015/, trendOptionsSource ], [ 'steady y-axis compact padding uses 0.985 for narrow above-one ranges', /STEADY_AXIS_COMPACT_SHRINK_RATIO\s*=\s*0\.985/, trendOptionsSource ], [ 'steady y-axis compact split penalty stays low', /STEADY_AXIS_COMPACT_EXTRA_SPLIT_SCORE\s*=\s*0\.05/, trendOptionsSource ], [ 'steady y-axis enables compact readable range for narrow above-one data', /shouldUseCompactReadableAxisRange[\s\S]*maxAbs\s*>\s*1[\s\S]*STEADY_AXIS_COMPACT_RANGE_RATIO/, trendOptionsSource ], ['shared phase colors read the global T phase theme variable', /T:\s*'--cn-color-phase-t'/, sharedPhaseColorSource], ['shared phase colors keep T phase black fallback', /T:\s*'#000000'/, sharedPhaseColorSource], ['shared phase colors map AB line voltage to phase A color', /AB:\s*'--cn-color-phase-ab'/, sharedPhaseColorSource], ['shared phase colors map BC line voltage to phase B color', /BC:\s*'--cn-color-phase-bc'/, sharedPhaseColorSource], ['shared phase colors map CA line voltage to phase C color', /CA:\s*'--cn-color-phase-ca'/, sharedPhaseColorSource], ['shared phase colors keep AB fallback aligned with phase A', /AB:\s*'#daa520'/, sharedPhaseColorSource], ['shared phase colors keep BC fallback aligned with phase B', /BC:\s*'#2e8b57'/, sharedPhaseColorSource], ['shared phase colors keep CA fallback aligned with phase C', /CA:\s*'#a52a2a'/, sharedPhaseColorSource], ['global style defines AB line voltage color variable', /--cn-color-phase-ab:\s*#daa520/, sharedStyleSource], ['global style defines BC line voltage color variable', /--cn-color-phase-bc:\s*#2e8b57/, sharedStyleSource], ['global style defines CA line voltage color variable', /--cn-color-phase-ca:\s*#a52a2a/, sharedStyleSource] ] const failures = expectations.filter(([, pattern, source]) => !pattern.test(source)) if (failures.length) { console.error('steadyDataView chart display contract failed:') for (const [name] of failures) { console.error(`- ${name}`) } process.exit(1) } console.log('steadyDataView chart display contract passed')