Files
2026-08-23 21:23:41 +08:00

99 lines
4.3 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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);