99 lines
4.3 KiB
JavaScript
99 lines
4.3 KiB
JavaScript
import * as echarts from 'echarts';
|
||
import fs from 'fs';
|
||
// 项目根目录(从 test/regress.mjs 向上一层,或直接指定绝对路径)
|
||
const P = '/Users/joey/sites/vest-tools/echart-demo/';
|
||
const {parseCSV}=await import(P+'js/csv.js');
|
||
const all=[
|
||
['inventory','inventoryChart','data/inventory.csv'],
|
||
['margin','marginChart','data/margin.csv'],
|
||
['twoline','twoLineChart','data/margin.csv'],
|
||
['growth','growthChart','data/growth.csv'],
|
||
['fx','fxChart','data/fx.csv'],
|
||
['ppmt_assets','assetsChart','data/ppmt_assets.csv'],
|
||
['ppmt_profit','profitChart','data/ppmt_profit.csv'],
|
||
['ppmt_ip','ipChart','data/ppmt_ip.csv'],
|
||
['ppmt_category','categoryChart','data/ppmt_category.csv'],
|
||
['ppmt_sankey','sankeyChart','data/ppmt_sankey_2026h1.csv'],
|
||
['ppmt_region_yoy','regionChannelYoyChart','data/ppmt_region_channel_yoy.csv'],
|
||
['ppmt_growth','ppmtGrowthChart','data/ppmt_growth.csv'],
|
||
['ppmt_margins','ppmtMarginsChart','data/ppmt_margins.csv'],
|
||
['ppmt_bridge','ppmtBridgeChart','data/ppmt_margin_bridge.csv'],
|
||
['ppmt_core_profit','ppmtCoreProfitChart','data/ppmt_core_profit.csv'],
|
||
['ppmt_core_margin','ppmtCoreMarginChart','data/ppmt_core_margin.csv'],
|
||
['ppmt_ip_share','ppmtIpShareChart','data/ppmt_ip_share_2026h1.csv'],
|
||
['ppmt_ip_share_history','ppmtIpShareHistoryChart','data/ppmt_ip_share_history.csv'],
|
||
['ppmt_ip_ex_monsters','ppmtIpExMonstersChart','data/ppmt_ip_ex_monsters.csv'],
|
||
];
|
||
|
||
// 可传图表名只跑指定的(改一张图不用全量回归):
|
||
// node test/regress.mjs ppmt_ip_share ppmt_core_margin
|
||
const only=process.argv.slice(2);
|
||
const targets=only.length>0 ? all.filter(([f])=>only.includes(f)) : all;
|
||
if(only.length>0){
|
||
const missing=only.filter(f=>!all.some(([name])=>name===f));
|
||
if(missing.length>0) console.log(`⚠ 未找到图表:${missing.join(', ')}`);
|
||
}
|
||
|
||
// 样式规范断言:从核心利润/利润率拆解图总结的标准
|
||
const STYLE_RULES={
|
||
legendTop:85, // 固定值:副标题到图例约25px
|
||
gridTop:145, // 固定值:图例到绘图区约60px
|
||
lineSeries:{ // 折线标准样式(空心圆点)
|
||
symbolSize:9,
|
||
borderWidth:2.5,
|
||
fill:'#fff', // 白心
|
||
},
|
||
};
|
||
|
||
let ok=0,fail=0,styleWarnings=[];
|
||
for(const [f,fn,csv] of targets){
|
||
try{
|
||
const mod=await import(P+`js/charts/${f}.js`);
|
||
const rows=parseCSV(fs.readFileSync(P+csv,'utf8'));
|
||
const c=echarts.init(null,null,{renderer:'svg',ssr:true,width:1100,height:720});
|
||
const opt=mod[fn](rows,{width:1100,height:720});
|
||
c.setOption(opt);
|
||
const svg=c.renderToSVGString();
|
||
|
||
// 1. SVG 格式检查(字体引号)
|
||
const badAttr=/style="[^"]*"[^"=>\s][^>]*>/.test(svg.slice(0,3000));
|
||
|
||
// 2. 样式规范检查(跳过无图例的图表)
|
||
if(opt.legend?.top!==undefined){
|
||
if(opt.legend.top!==STYLE_RULES.legendTop){
|
||
styleWarnings.push(`${f}: legend.top=${opt.legend.top} (期望${STYLE_RULES.legendTop})`);
|
||
}
|
||
}
|
||
if(opt.grid?.top!==undefined && opt.legend?.top!==undefined){
|
||
if(opt.grid.top!==STYLE_RULES.gridTop){
|
||
styleWarnings.push(`${f}: grid.top=${opt.grid.top} (期望${STYLE_RULES.gridTop})`);
|
||
}
|
||
}
|
||
|
||
// 3. 折线圆点样式检查(在 SVG 里验证 symbolSize 和 borderWidth)
|
||
const circles=[...svg.matchAll(/<path d="M1 0A1[^"]*" transform="matrix\(([\d.]+),0,0,[\d.]+,[^)]+\)" fill="#fff" stroke="[^"]*" stroke-width="([\d.]+)"/g)];
|
||
if(circles.length>0){
|
||
const scale=+circles[0][1], sw=+circles[0][2];
|
||
const symbolSize=scale*2, borderWidth=sw*scale;
|
||
if(Math.abs(symbolSize-STYLE_RULES.lineSeries.symbolSize)>0.1){
|
||
styleWarnings.push(`${f}: 折线 symbolSize=${symbolSize.toFixed(1)} (期望${STYLE_RULES.lineSeries.symbolSize})`);
|
||
}
|
||
if(Math.abs(borderWidth-STYLE_RULES.lineSeries.borderWidth)>0.1){
|
||
styleWarnings.push(`${f}: 折线 borderWidth=${borderWidth.toFixed(1)} (期望${STYLE_RULES.lineSeries.borderWidth})`);
|
||
}
|
||
}
|
||
|
||
console.log(`${badAttr?'⚠':'✓'} ${f.padEnd(17)} ${String(svg.length).padStart(6)}B`);
|
||
ok++; c.dispose();
|
||
}catch(e){console.log(`✗ ${f.padEnd(17)} ${e.message}`);fail++;}
|
||
}
|
||
console.log(`\n${ok} 张成功 / ${fail} 张失败`);
|
||
|
||
if(styleWarnings.length>0){
|
||
console.log(`\n⚠ ${styleWarnings.length} 处样式偏离规范:`);
|
||
styleWarnings.forEach(w=>console.log(` ${w}`));
|
||
}else{
|
||
console.log('✓ 样式规范全部符合');
|
||
}
|
||
process.exit(fail>0?1:0);
|