Files
kyugao ec02a791ee 迭代22+23: 策略tab静态化 + 计算字段(数字/判定型)+ 网格超市信号字段
- R-027 静态化:策略恒为内置两项(不可增删改名,保留显隐/排序);设置页移除「策略分组」;
  字段配置入口迁到策略 tab 内「列设置」旁(FieldConfigDialog + schema-update 单策略写入)
- R-026 计算字段:自写公式引擎(中文变量、四则/括号/round·abs·min·max、缺值短路→—)+
  变量目录(行情/合约/持仓/自定义字段)+ strategy-positions 逐行现算(只读内存缓存,不落库、列只读)
- R-028 判定型:比较运算 + and/or + inferResultKind + 结果类型一致性校验 + 判定列 ✓/— 渲染;
  网格超市落地 可下空单=涨停价>基准值+网格大小、可下多单=跌停价<基准值-网格大小
- 变量选择改标签平铺(老师反馈);公式手册 docs/99-其他材料/计算字段公式说明.md
- 约束同步:产品约束-002/003/004/009/013/014、技术约束-014/015/022/023/024、UI约束-002/003/005/008
- 回归:新增 test-r026/test-r027,更新 r011/r013/r017,17 个脚本全绿;typecheck/build 通过
2026-09-10 14:05:06 +08:00

116 lines
6.6 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.
/**
* R-011 回归测试:tabs 统一管理数据层(R-027 策略 tab 静态化)
* 独立内存 mock scopesettings 读写),验证:
* 1. 归一化迁移:旧布尔对象 tabs → 有序数组(内置保留原显隐,策略接续追加)
* 2. 迁移:旧隐藏策略 → visible=trueQ3
* 3. getTabs:策略行 name 由内置常量派生(改名场景已退役)
* 4. updateTabs:整表更新(顺序 + 显隐)
* 5. 策略 tab 静态化(R-027):getStrategies 恒 2 项;normalizeTabs 恒 5 条;
* 存量自建策略 tab 条目被丢弃;addStrategy/removeStrategy/renameStrategy 等退役导出不存在
* 纯内存,不触真实数据(技术约束-011)
*/
import { getTabs, updateTabs, normalizeTabs, getStrategies } from '../src/settings.js';
import * as settingsMod from '../src/settings.js';
let pass = 0, fail = 0;
function assert(cond, msg) {
if (cond) { pass++; console.log(" ✅ " + msg); }
else { fail++; console.log(" ❌ " + msg); }
}
/** 内存 mock scopeget/update 读改写对象 */
function makeScope(initial) {
const store = structuredClone(initial ?? {});
return {
get: () => structuredClone(store),
update: async (patch) => { Object.assign(store, structuredClone(patch)); },
_store: store,
};
}
// ---------- 1. 旧格式归一化(布尔对象 tabs + 策略带 visible/order ----------
console.log("\n[1] 旧格式归一化迁移");
const oldScope = makeScope({
strategies: [
{ id: 'grid-supermarket', name: '网格超市', visible: true, order: 5 },
{ id: 'manual-t', name: '手动做T', visible: false, order: 9 },
],
tabs: { allPositions: true, tradeRecords: false, watchlist: true },
});
const oldTabs = normalizeTabs(oldScope);
assert(oldTabs.length === 5, "归一化后共 5 个条目(3 内置 + 2 策略)");
assert(oldTabs[0].kind === 'builtin' && oldTabs[0].refKey === 'allPositions', '内置 allPositions 在前');
assert(oldTabs[1].refKey === 'tradeRecords' && oldTabs[1].visible === false, '内置 tradeRecords 保留旧显隐=false');
assert(oldTabs[2].refKey === 'watchlist' && oldTabs[2].visible === true, '内置 watchlist 保留旧显隐=true');
const manualTab = oldTabs.find((t) => t.kind === 'strategy' && t.refId === 'manual-t');
assert(manualTab && manualTab.visible === true, "旧隐藏策略 manual-t 迁移后 visible=trueQ3");
assert(manualTab && manualTab.name === '手动做T', '策略行 name 来自内置常量');
assert(oldTabs[3].order === 3 && oldTabs[4].order === 4, '策略按 order 接续(order 连续)');
// ---------- 2. 已归一化数组直接返回 ----------
console.log("\n[2] 已归一化数组直接返回");
const arrScope = makeScope({ tabs: [
{ id: 'tab-all-positions', kind: 'builtin', refKey: 'allPositions', name: '全部持仓', visible: true, order: 1 },
{ id: 'tab-watchlist', kind: 'builtin', refKey: 'watchlist', name: '关注列表', visible: true, order: 0 },
] });
const arrTabs = normalizeTabs(arrScope);
assert(arrTabs[0].id === 'tab-watchlist' && arrTabs[1].id === 'tab-all-positions', '数组按 order 排序');
assert(arrTabs.length === 5, '缺失条目补齐(恒 5 条)');
// ---------- 3. getTabs:策略行 name 由常量派生(改名已退役) ----------
console.log("\n[3] getTabs 策略行 name");
const joinScope = makeScope({
strategies: [{ id: 'manual-t', name: '手动做T新名' }], // 存量改名残留:不再参与
tabs: [
{ id: 'tab-all-positions', kind: 'builtin', refKey: 'allPositions', name: '全部持仓', visible: true, order: 0 },
{ id: 'tab-strategy-manual-t', kind: 'strategy', refId: 'manual-t', name: '旧名', visible: true, order: 1 },
],
});
const joined = getTabs(joinScope);
assert(joined.find((t) => t.id === 'tab-strategy-manual-t').name === '手动做T', '策略行 name 恒为内置常量(存量行 name 被覆盖)');
// ---------- 4. updateTabs 整表更新 ----------
console.log("\n[4] updateTabs 整表更新");
const updScope = makeScope({ tabs: [
{ id: 'tab-all-positions', kind: 'builtin', refKey: 'allPositions', name: '全部持仓', visible: true, order: 0 },
{ id: 'tab-watchlist', kind: 'builtin', refKey: 'watchlist', name: '关注列表', visible: true, order: 1 },
] });
await updateTabs(updScope, [
{ id: 'tab-watchlist', kind: 'builtin', refKey: 'watchlist', name: '关注列表', visible: false, order: 0 },
{ id: 'tab-all-positions', kind: 'builtin', refKey: 'allPositions', name: '全部持仓', visible: true, order: 1 },
]);
const updTabs = getTabs(updScope);
assert(updTabs[0].id === 'tab-watchlist' && updTabs[0].visible === false, '整表更新:顺序与显隐生效');
assert(updTabs.length === 5, '归一化后恒 5 条(缺失策略行补齐)');
// ---------- 5. 策略 tab 静态化(R-027 ----------
console.log("\n[5] 策略 tab 静态化(R-027");
const statScope = makeScope({
strategies: [
{ id: 'grid-supermarket', name: '网格超市' },
{ id: 'manual-t', name: '手动做T' },
{ id: 'long-term', name: '长线持有' }, // R-027 前可增:存量自建策略
],
tabs: [
{ id: 'tab-all-positions', kind: 'builtin', refKey: 'allPositions', name: '全部持仓', visible: true, order: 1 },
{ id: 'tab-watchlist', kind: 'builtin', refKey: 'watchlist', name: '关注列表', visible: true, order: 2 },
{ id: 'tab-strategy-long-term', kind: 'strategy', refId: 'long-term', name: '长线持有', visible: true, order: 0 }, // 存量自建策略 tab
],
});
const strat = getStrategies(statScope);
assert(strat.length === 2, 'getStrategies 恒 2 项(自建策略被忽略)');
assert(!strat.some((s) => s.id === 'long-term'), '自建策略不出现在读取结果');
const statTabs = normalizeTabs(statScope);
assert(statTabs.length === 5, 'normalizeTabs 恒 5 条(3 内置 + 2 策略)');
assert(!statTabs.some((t) => t.refId === 'long-term'), '存量自建策略 tab 条目被丢弃');
assert(statTabs.filter((t) => t.kind === 'builtin').length === 3 && statTabs.filter((t) => t.kind === 'strategy').length === 2, '条目构成 = 3 内置 + 2 策略');
assert(!('addStrategy' in settingsMod), '退役导出 addStrategy 不存在');
assert(!('removeStrategy' in settingsMod), '退役导出 removeStrategy 不存在');
assert(!('renameStrategy' in settingsMod), '退役导出 renameStrategy 不存在');
assert(!('updateStrategies' in settingsMod), '退役导出 updateStrategies 不存在');
assert(!('appendStrategyTab' in settingsMod) && !('removeStrategyTab' in settingsMod), '退役导出 append/removeStrategyTab 不存在');
// ---------- 汇总 ----------
console.log("\n结果: " + pass + " 通过 / " + fail + " 失败");
if (fail > 0) process.exit(1);