513 lines
25 KiB
JavaScript
513 lines
25 KiB
JavaScript
|
|
/* 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 => {
|
||
|
|
const filePath = path.join(viewDir, file)
|
||
|
|
|
||
|
|
return fs.existsSync(filePath) ? fs.readFileSync(filePath, 'utf8') : ''
|
||
|
|
}
|
||
|
|
|
||
|
|
const toolbarSource = read('components/SteadyTrendToolbar.vue')
|
||
|
|
const chartPanelSource = read('components/SteadyTrendChartPanel.vue')
|
||
|
|
const fullscreenSource = read('components/SteadyTrendFullscreen.vue')
|
||
|
|
const chartToolsSource = read('components/SteadyTrendChartTools.vue')
|
||
|
|
const chartRendererSource = read('components/SteadyTrendChartRenderer.vue')
|
||
|
|
const lineChartSource = fs.readFileSync(path.join(viewDir, '..', '..', '..', 'components', 'echarts', 'line', 'index.vue'), 'utf8')
|
||
|
|
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',
|
||
|
|
/<el-select(?![\s\S]*multiple[\s\S]*placeholder="[^"]*统计类型")[\s\S]*placeholder="[^"]*统计类型"/,
|
||
|
|
toolbarSource
|
||
|
|
],
|
||
|
|
[
|
||
|
|
'trend payload keeps API statTypes array but only sends active stat',
|
||
|
|
/statTypes:\s*\[formState\.statType\]/,
|
||
|
|
trendPayloadSource
|
||
|
|
],
|
||
|
|
[
|
||
|
|
'selection rules cap monitoring points at six',
|
||
|
|
/MAX_SELECTED_LINE_COUNT\s*=\s*6[\s\S]*lineIds\.length\s*>\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 through the renderer adapter',
|
||
|
|
/v-for="group in chartGroups"[\s\S]*<SteadyTrendChartRenderer[\s\S]*:group="group"/,
|
||
|
|
chartPanelSource
|
||
|
|
],
|
||
|
|
[
|
||
|
|
'chart loading mask stays outside the fullscreen dialog tree',
|
||
|
|
/<section\s+class="card trend-chart-panel"(?![^>]*v-loading)[\s\S]*<div[^>]*class="chart-panel-body"[^>]*v-loading="loading"[\s\S]*<SteadyTrendFullscreen/,
|
||
|
|
chartPanelSource
|
||
|
|
],
|
||
|
|
[
|
||
|
|
'normal chart list caps visible charts at three while smaller counts fill the viewport',
|
||
|
|
/(?=[\s\S]*normalVisibleChartCount[\s\S]*Math\.min\(chartGroups\.value\.length,\s*3\))(?=[\s\S]*:style="\{\s*'--steady-trend-visible-chart-count':\s*normalVisibleChartCount\s*\}")/,
|
||
|
|
chartPanelSource
|
||
|
|
],
|
||
|
|
[
|
||
|
|
'fullscreen chart list caps visible charts at six while smaller counts fill the viewport',
|
||
|
|
/(?=[\s\S]*fullscreenVisibleChartCount[\s\S]*Math\.min\(chartGroups\.value\.length,\s*6\))(?=[\s\S]*:visible-chart-count="fullscreenVisibleChartCount")/,
|
||
|
|
chartPanelSource
|
||
|
|
],
|
||
|
|
[
|
||
|
|
'chart groups divide the current viewport by visible chart count',
|
||
|
|
/\.chart-group\s*\{[\s\S]*flex:\s*0 0\s*calc\(\s*\(100% - var\(--steady-trend-chart-gap\)\s*\*\s*\(var\(--steady-trend-visible-chart-count\)\s*-\s*1\)\)\s*\/\s*var\(--steady-trend-visible-chart-count\)\s*\)/,
|
||
|
|
chartPanelSource
|
||
|
|
],
|
||
|
|
[
|
||
|
|
'chart groups include their border in the divided height to avoid false scrollbars',
|
||
|
|
/\.chart-group\s*\{[\s\S]*box-sizing:\s*border-box/,
|
||
|
|
chartPanelSource
|
||
|
|
],
|
||
|
|
[
|
||
|
|
'chart panel delegates fullscreen rendering to a dedicated component',
|
||
|
|
/<SteadyTrendFullscreen[\s\S]*v-model="fullscreenVisible"[\s\S]*:chart-groups="chartGroups"[\s\S]*:visible-chart-count="fullscreenVisibleChartCount"[\s\S]*@chart-data-zoom="handleChartDataZoom"/,
|
||
|
|
chartPanelSource
|
||
|
|
],
|
||
|
|
[
|
||
|
|
'chart panel passes shared trend tools into fullscreen',
|
||
|
|
/<SteadyTrendFullscreen[\s\S]*:tool-groups="fullscreenToolGroups"[\s\S]*:is-tool-active="isTrendToolActive"[\s\S]*:is-tool-disabled="isTrendToolDisabled"[\s\S]*:get-tool-tooltip="getTrendToolTooltip"[\s\S]*@tool-action="handleTrendToolAction"/,
|
||
|
|
chartPanelSource
|
||
|
|
],
|
||
|
|
[
|
||
|
|
'fullscreen tool groups omit nested fullscreen action',
|
||
|
|
/fullscreenToolGroups[\s\S]*item\.action\s*!==\s*'fullscreen'/,
|
||
|
|
chartPanelSource
|
||
|
|
],
|
||
|
|
[
|
||
|
|
'chart panel does not use Element Plus dialog for steady fullscreen',
|
||
|
|
/^(?![\s\S]*<el-dialog[\s\S]*steady-trend-fullscreen)(?![\s\S]*fullscreen-chart-body)[\s\S]*$/,
|
||
|
|
chartPanelSource
|
||
|
|
],
|
||
|
|
[
|
||
|
|
'fullscreen component renders through a fixed body teleport',
|
||
|
|
/<Teleport\s+to="body">[\s\S]*class="steady-trend-fullscreen"[\s\S]*position:\s*fixed[\s\S]*inset:\s*0/,
|
||
|
|
fullscreenSource
|
||
|
|
],
|
||
|
|
[
|
||
|
|
'fullscreen component has an explicit close button and Esc handling',
|
||
|
|
/@click="closeFullscreen"[\s\S]*event\.key\s*!==\s*'Escape'[\s\S]*window\.addEventListener\('keydown',\s*handleKeydown\)/,
|
||
|
|
fullscreenSource
|
||
|
|
],
|
||
|
|
[
|
||
|
|
'fullscreen component gives chart body a concrete flex viewport',
|
||
|
|
/\.steady-trend-fullscreen__body\s*\{[\s\S]*display:\s*flex[\s\S]*flex:\s*1\s+1\s+auto[\s\S]*min-height:\s*0[\s\S]*\.steady-trend-fullscreen__chart-list\s*\{[\s\S]*flex:\s*1\s+1\s+auto[\s\S]*overflow-y:\s*auto/,
|
||
|
|
fullscreenSource
|
||
|
|
],
|
||
|
|
[
|
||
|
|
'fullscreen keeps trend tool groups in a real workspace row below the title bar',
|
||
|
|
/<main class="steady-trend-fullscreen__body">[\s\S]*<div class="steady-trend-fullscreen__tool-row">[\s\S]*<SteadyTrendChartTools[\s\S]*<\/div>[\s\S]*class="steady-trend-fullscreen__chart-list"/,
|
||
|
|
fullscreenSource
|
||
|
|
],
|
||
|
|
[
|
||
|
|
'fullscreen title header does not contain trend tool groups',
|
||
|
|
/<header class="steady-trend-fullscreen__header">(?:(?!SteadyTrendChartTools)[\s\S])*<\/header>/,
|
||
|
|
fullscreenSource
|
||
|
|
],
|
||
|
|
[
|
||
|
|
'fullscreen tool row reserves layout space instead of floating over charts',
|
||
|
|
/\.steady-trend-fullscreen__tool-row\s*\{(?=[\s\S]*position:\s*static)(?=[\s\S]*display:\s*flex)(?=[\s\S]*flex:\s*none)[\s\S]*\}/,
|
||
|
|
fullscreenSource
|
||
|
|
],
|
||
|
|
[
|
||
|
|
'chart renderer uses SciChart for steadyTrend charts',
|
||
|
|
/<SteadyTrendSciChart[\s\S]*:series-list="group\.seriesList"[\s\S]*@chart-data-zoom="emit\('chart-data-zoom', \$event\)"/,
|
||
|
|
chartRendererSource
|
||
|
|
],
|
||
|
|
[
|
||
|
|
'chart renderer does not import ECharts LineChart',
|
||
|
|
/^(?![\s\S]*LineChart)[\s\S]*$/,
|
||
|
|
chartRendererSource
|
||
|
|
],
|
||
|
|
[
|
||
|
|
'chart panel shows total point count with explicit label',
|
||
|
|
/总点数:\{\{\s*trendResult\.displayPointCount\s*\|\|\s*0\s*\}\}/,
|
||
|
|
chartPanelSource
|
||
|
|
],
|
||
|
|
[
|
||
|
|
'chart panel does not render bucket slash prefix before point count',
|
||
|
|
/^(?![\s\S]*trendResult\.bucket[\s\S]*\/[\s\S]*trendResult\.displayPointCount)[\s\S]*$/,
|
||
|
|
chartPanelSource
|
||
|
|
],
|
||
|
|
[
|
||
|
|
'chart panel renders shared trend tool groups',
|
||
|
|
/<SteadyTrendChartTools[\s\S]*:tool-groups="trendToolGroups"[\s\S]*@tool-action="handleTrendToolAction"/,
|
||
|
|
chartPanelSource
|
||
|
|
],
|
||
|
|
[
|
||
|
|
'chart panel keeps total point count on the right of the trend toolbar with 15px spacing',
|
||
|
|
/\.panel-meta\s*\{[\s\S]*margin-left:\s*15px/,
|
||
|
|
chartPanelSource
|
||
|
|
],
|
||
|
|
[
|
||
|
|
'shared chart tools component renders the trend tool buttons',
|
||
|
|
/trend-tool-groups[\s\S]*v-for="group in toolGroups"[\s\S]*v-for="item in group\.items"[\s\S]*@click="emit\('tool-action', item\.action\)"/,
|
||
|
|
chartToolsSource
|
||
|
|
],
|
||
|
|
[
|
||
|
|
'fullscreen renders shared trend tool groups',
|
||
|
|
/<SteadyTrendChartTools[\s\S]*:tool-groups="toolGroups"[\s\S]*@tool-action="emit\('tool-action', \$event\)"/,
|
||
|
|
fullscreenSource
|
||
|
|
],
|
||
|
|
[
|
||
|
|
'chart panel exposes core trend toolbar actions except marker and data export',
|
||
|
|
/x-zoom-in[\s\S]*x-zoom-out[\s\S]*y-zoom-in[\s\S]*y-zoom-out[\s\S]*box-zoom[\s\S]*reset[\s\S]*pan[\s\S]*fullscreen[\s\S]*download-image/,
|
||
|
|
chartPanelSource
|
||
|
|
],
|
||
|
|
[
|
||
|
|
'chart panel exposes a wheel zoom toggle action',
|
||
|
|
/wheel-zoom/,
|
||
|
|
chartPanelSource
|
||
|
|
],
|
||
|
|
[
|
||
|
|
'chart panel places wheel zoom after pan in the viewport toolbar',
|
||
|
|
/items:\s*\[[\s\S]*action:\s*'pan'[\s\S]*action:\s*'wheel-zoom'/,
|
||
|
|
chartPanelSource
|
||
|
|
],
|
||
|
|
[
|
||
|
|
'chart panel defaults wheel zoom disabled',
|
||
|
|
/const\s+wheelZoomEnabled\s*=\s*ref\(false\)/,
|
||
|
|
chartPanelSource
|
||
|
|
],
|
||
|
|
[
|
||
|
|
'chart panel passes wheel zoom state into grouped options',
|
||
|
|
/buildSteadyTrendChartGroups\([^)]*trendXZoomRange\.value[\s\S]*wheelZoomEnabled:\s*wheelZoomEnabled\.value/,
|
||
|
|
chartPanelSource
|
||
|
|
],
|
||
|
|
[
|
||
|
|
'chart panel keeps full range as fallback x zoom range',
|
||
|
|
/DEFAULT_STEADY_TREND_X_ZOOM_RANGE\s*:\s*SteadyTrendZoomRange\s*=\s*\{\s*start:\s*0,\s*end:\s*100\s*\}/,
|
||
|
|
chartPanelSource
|
||
|
|
],
|
||
|
|
[
|
||
|
|
'chart panel defaults x zoom range by 20 30 60 day thresholds',
|
||
|
|
/STEADY_TREND_HALF_RANGE_DAYS\s*=\s*20[\s\S]*STEADY_TREND_QUARTER_RANGE_DAYS\s*=\s*30[\s\S]*STEADY_TREND_TENTH_RANGE_DAYS\s*=\s*60[\s\S]*resolveSteadyTrendDefaultZoomRange[\s\S]*timeRangeDays\s*>\s*STEADY_TREND_TENTH_RANGE_DAYS[\s\S]*end:\s*10[\s\S]*timeRangeDays\s*>\s*STEADY_TREND_QUARTER_RANGE_DAYS[\s\S]*end:\s*25[\s\S]*timeRangeDays\s*>\s*STEADY_TREND_HALF_RANGE_DAYS[\s\S]*end:\s*50/,
|
||
|
|
chartPanelSource
|
||
|
|
],
|
||
|
|
[
|
||
|
|
'chart panel stores shared x zoom range from the default range',
|
||
|
|
/const trendXZoomRange\s*=\s*ref<SteadyTrendZoomRange>\(\{\s*\.\.\.DEFAULT_STEADY_TREND_X_ZOOM_RANGE\s*\}\)/,
|
||
|
|
chartPanelSource
|
||
|
|
],
|
||
|
|
[
|
||
|
|
'chart panel compares reset state against the current data-derived x zoom range',
|
||
|
|
/const defaultTrendXZoomRange\s*=\s*ref<SteadyTrendZoomRange>[\s\S]*const isDefaultTrendXZoomRange[\s\S]*defaultTrendXZoomRange\.value[\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<SteadyTrendInteractionMode>\('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 current data-derived default when trend result changes',
|
||
|
|
/const\s+resetTrendToolState\s*=\s*\(\)\s*=>\s*\{[\s\S]*trendXZoomRange\.value\s*=\s*\{\s*\.\.\.defaultTrendXZoomRange\.value\s*\}[\s\S]*watch\(\s*\(\)\s*=>\s*props\.trendResult[\s\S]*defaultTrendXZoomRange\.value\s*=\s*resolveSteadyTrendDefaultZoomRange\(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 enlarge slider dataZoom handles for easier horizontal dragging',
|
||
|
|
/handleSize:\s*'300%'/,
|
||
|
|
trendOptionsSource
|
||
|
|
],
|
||
|
|
[
|
||
|
|
'chart options highlight slider dataZoom handles on hover',
|
||
|
|
/emphasis:\s*\{[\s\S]*handleStyle:\s*\{[\s\S]*borderColor:\s*'#409eff'[\s\S]*shadowBlur:\s*6/,
|
||
|
|
trendOptionsSource
|
||
|
|
],
|
||
|
|
[
|
||
|
|
'chart options show pointer cursor on slider dataZoom handles',
|
||
|
|
/handleSize:\s*'300%'[\s\S]*cursor:\s*'pointer'[\s\S]*handleStyle:/,
|
||
|
|
trendOptionsSource
|
||
|
|
],
|
||
|
|
[
|
||
|
|
'chart slider dataZoom only syncs after dragging settles for dense trend charts',
|
||
|
|
/handleSize:\s*'300%'[\s\S]*realtime:\s*false[\s\S]*cursor:\s*'pointer'/,
|
||
|
|
trendOptionsSource
|
||
|
|
],
|
||
|
|
[
|
||
|
|
'line chart overrides slider dataZoom handle cursor to pointer on hover',
|
||
|
|
/isSliderDataZoomResizeHandle[\s\S]*target\?\.type\s*===\s*'path'[\s\S]*viewportRoot\.style\.cursor\s*=\s*'pointer'/,
|
||
|
|
lineChartSource
|
||
|
|
],
|
||
|
|
[
|
||
|
|
'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 hide hover x-axis pointer labels to avoid overlapping time axis labels',
|
||
|
|
/tooltip:\s*\{[\s\S]*axisPointer:\s*\{[\s\S]*type:\s*'line'[\s\S]*label:\s*\{[\s\S]*show:\s*false/,
|
||
|
|
trendOptionsSource
|
||
|
|
],
|
||
|
|
[
|
||
|
|
'chart tooltip avoids heavy immediate transitions on dense trend charts',
|
||
|
|
/tooltip:\s*\{[\s\S]*showDelay:\s*80[\s\S]*hideDelay:\s*80[\s\S]*transitionDuration:\s*0/,
|
||
|
|
trendOptionsSource
|
||
|
|
],
|
||
|
|
[
|
||
|
|
'chart options register hidden toolbox dataZoom for external box zoom',
|
||
|
|
/toolbox:\s*\{[\s\S]*show:\s*true[\s\S]*itemSize:\s*0[\s\S]*left:\s*-100[\s\S]*feature:\s*\{[\s\S]*dataZoom:\s*\{[\s\S]*yAxisIndex:\s*'none'[\s\S]*brushStyle:/,
|
||
|
|
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',
|
||
|
|
/const\s+pointCount\s*=\s*series\.points\?\.length\s*\|\|\s*0[\s\S]*width:\s*resolveSteadyTrendLineWidth\(\s*resolveSteadyTrendVisiblePointCount\(pointCount,\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]*<div class="chart-title">)[\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 shows daily first points for ranges over one day and minute-level endpoints otherwise',
|
||
|
|
/STEADY_TREND_ONE_DAY_MS[\s\S]*resolveSteadyTimeAxisLabelMeta[\s\S]*firstDailyLabelIndexSet[\s\S]*formatSteadyTimeAxisDateLabel[\s\S]*formatSteadyTimeAxisShortDateLabel[\s\S]*formatSteadyTimeAxisMinuteLabel/,
|
||
|
|
trendOptionsSource
|
||
|
|
],
|
||
|
|
[
|
||
|
|
'chart x axis keeps only the first year for same-year daily labels',
|
||
|
|
/firstYear[\s\S]*formatSteadyTimeAxisDailyLabel[\s\S]*date\.getFullYear\(\)\s*===\s*labelMeta\.firstYear[\s\S]*formatSteadyTimeAxisShortDateLabel/,
|
||
|
|
trendOptionsSource
|
||
|
|
],
|
||
|
|
[
|
||
|
|
'chart x axis first label uses rich padding to avoid touching the y axis line',
|
||
|
|
/formatSteadyTimeAxisFirstLabel[\s\S]*return `\{first\|\$\{label\}\}`[\s\S]*rich:\s*\{[\s\S]*first:\s*\{[\s\S]*padding:\s*\[0,\s*0,\s*0,\s*6\]/,
|
||
|
|
trendOptionsSource
|
||
|
|
],
|
||
|
|
[
|
||
|
|
'chart x axis label layout follows waveform except unit display',
|
||
|
|
/axisLabel:\s*\{[\s\S]*hideOverlap:\s*true[\s\S]*showMinLabel:\s*true[\s\S]*showMaxLabel:\s*false[\s\S]*interval:\s*0[\s\S]*margin:\s*showTimeAxis\s*\?\s*16\s*:\s*0[\s\S]*alignMinLabel:\s*'left'[\s\S]*alignMaxLabel:\s*'right'[\s\S]*width:\s*72[\s\S]*overflow:\s*'truncate'[\s\S]*formatter:\s*buildSteadyTimeAxisLabelFormatter\(timeLabels\)/,
|
||
|
|
trendOptionsSource
|
||
|
|
],
|
||
|
|
[
|
||
|
|
'chart x axis label moves down without increasing bottom grid space',
|
||
|
|
/bottom:\s*showTimeAxis\s*\?\s*40\s*:\s*8[\s\S]*axisLabel:\s*\{[\s\S]*margin:\s*showTimeAxis\s*\?\s*16\s*:\s*0/,
|
||
|
|
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 combines harmonic order and phase when harmonic order exists',
|
||
|
|
/const formatSeriesName[\s\S]*const harmonicOrder = resolveHarmonicOrder\(series\)[\s\S]*return harmonicOrder \? `\$\{harmonicOrder\}次_\$\{phaseLabel\}` : phaseLabel/,
|
||
|
|
trendOptionsSource
|
||
|
|
],
|
||
|
|
[
|
||
|
|
'chart series are sorted by harmonic order before phase so same orders stay together in the legend',
|
||
|
|
/const sortSteadyTrendSeries[\s\S]*resolveHarmonicOrder\(left\.series\)[\s\S]*resolvePhaseOrder\(left\.series\)[\s\S]*const sortedSeriesList = sortSteadyTrendSeries\((?:seriesList|displaySeriesList)\)[\s\S]*series: sortedSeriesList\.map/,
|
||
|
|
trendOptionsSource
|
||
|
|
],
|
||
|
|
[
|
||
|
|
'chart options keep phase colors while styling harmonic orders by line type',
|
||
|
|
/lineStyle:\s*\{[\s\S]*type:\s*resolveHarmonicLineType\(series,\s*pointCount\)[\s\S]*opacity:\s*resolveHarmonicLineOpacity\(series\)/,
|
||
|
|
trendOptionsSource
|
||
|
|
],
|
||
|
|
[
|
||
|
|
'chart options force solid harmonic lines for large point counts',
|
||
|
|
/STEADY_TREND_LARGE_POINT_COUNT\s*=\s*20000[\s\S]*resolveHarmonicLineType[\s\S]*pointCount\s*>=\s*STEADY_TREND_LARGE_POINT_COUNT[\s\S]*return 'solid'/,
|
||
|
|
trendOptionsSource
|
||
|
|
],
|
||
|
|
[
|
||
|
|
'chart series disable animation and use lttb sampling for large point counts',
|
||
|
|
/resolveSteadyTrendSampling[\s\S]*pointCount\s*>=\s*STEADY_TREND_LARGE_POINT_COUNT\s*\?\s*'lttb'[\s\S]*animation:\s*false[\s\S]*sampling:\s*resolveSteadyTrendSampling\(pointCount\)/,
|
||
|
|
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('steadyTrend chart display contract failed:')
|
||
|
|
for (const [name] of failures) {
|
||
|
|
console.error(`- ${name}`)
|
||
|
|
}
|
||
|
|
process.exit(1)
|
||
|
|
}
|
||
|
|
|
||
|
|
console.log('steadyTrend chart display contract passed')
|